ng-easycommerce-v18 0.3.20-beta.2 → 0.3.21-beta.2

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.
Files changed (29) hide show
  1. package/README.md +4 -16
  2. package/esm2022/lib/classes/filters/attributes-filter.mjs +4 -74
  3. package/esm2022/lib/classes/filters/category-filter.mjs +26 -105
  4. package/esm2022/lib/classes/filters/filter-factory.mjs +3 -7
  5. package/esm2022/lib/classes/filters/price_range-filter.mjs +3 -3
  6. package/esm2022/lib/constants/core.constants.service.mjs +1 -12
  7. package/esm2022/lib/ec-components/checkout-ec/payment-ec/payment-ec.component.mjs +6 -1
  8. package/esm2022/lib/ec-components/checkout-ec/payment-ec/payment-methods/mp-redirect-ec/mp-redirect-ec.component.mjs +53 -44
  9. package/esm2022/lib/ec-components/collection-ec/collection-ec.component.mjs +17 -41
  10. package/esm2022/lib/ec-components/filters-ec/filters-ec.component.mjs +9 -42
  11. package/esm2022/lib/ec-components/header-ec/header-ec.component.mjs +5 -13
  12. package/esm2022/lib/ec-components/price-range-filter/price-range-filter.component.mjs +2 -13
  13. package/esm2022/lib/ec-components/widgets-ec/redsys-catch-ec/redsys-catch-ec.component.mjs +17 -7
  14. package/esm2022/lib/ec-services/filters.service.mjs +18 -124
  15. package/esm2022/lib/ec-services/pagination.service.mjs +22 -70
  16. package/esm2022/lib/ec-services/products.service.mjs +3 -5
  17. package/fesm2022/ng-easycommerce-v18.mjs +170 -540
  18. package/fesm2022/ng-easycommerce-v18.mjs.map +1 -1
  19. package/lib/classes/filters/attributes-filter.d.ts +0 -24
  20. package/lib/classes/filters/category-filter.d.ts +3 -30
  21. package/lib/constants/core.constants.service.d.ts +0 -7
  22. package/lib/ec-components/checkout-ec/payment-ec/payment-methods/mp-redirect-ec/mp-redirect-ec.component.d.ts +1 -0
  23. package/lib/ec-components/collection-ec/collection-ec.component.d.ts +4 -5
  24. package/lib/ec-components/filters-ec/filters-ec.component.d.ts +0 -13
  25. package/lib/ec-components/price-range-filter/price-range-filter.component.d.ts +0 -2
  26. package/lib/ec-services/filters.service.d.ts +1 -18
  27. package/lib/ec-services/pagination.service.d.ts +5 -21
  28. package/lib/ec-services/products.service.d.ts +1 -1
  29. package/package.json +1 -1
@@ -1,8 +1,8 @@
1
1
  import * as i0 from '@angular/core';
2
- import { InjectionToken, makeEnvironmentProviders, inject, Injectable, PLATFORM_ID, RendererFactory2, afterNextRender, signal, EnvironmentInjector, runInInjectionContext, Component, ChangeDetectorRef, HostListener, CUSTOM_ELEMENTS_SCHEMA, Input, Pipe, Injector, EventEmitter, Output, forwardRef, afterRender, ViewChild, Inject, computed, Renderer2, ChangeDetectionStrategy, Directive } from '@angular/core';
2
+ import { InjectionToken, makeEnvironmentProviders, inject, Injectable, PLATFORM_ID, RendererFactory2, afterNextRender, signal, EnvironmentInjector, runInInjectionContext, Component, ChangeDetectorRef, HostListener, CUSTOM_ELEMENTS_SCHEMA, Input, Pipe, Injector, EventEmitter, Output, forwardRef, afterRender, ViewChild, Inject, computed, NgZone, Renderer2, ChangeDetectionStrategy, Directive } from '@angular/core';
3
3
  import * as i1 from '@angular/common';
4
4
  import { DOCUMENT, isPlatformBrowser, AsyncPipe, CommonModule, TitleCasePipe, JsonPipe, UpperCasePipe, Location } from '@angular/common';
5
- import { take, BehaviorSubject, shareReplay, map, catchError, of, filter, ReplaySubject, firstValueFrom, concatMap, throwError, tap, distinctUntilChanged, switchMap, combineLatest, Subject, takeUntil } from 'rxjs';
5
+ import { take, BehaviorSubject, shareReplay, map, catchError, of, filter, ReplaySubject, firstValueFrom, concatMap, throwError, switchMap, combineLatest } from 'rxjs';
6
6
  import { HttpClient, HttpHeaders } from '@angular/common/http';
7
7
  import * as i1$1 from '@ngx-translate/core';
8
8
  import { TranslateService, TranslateModule } from '@ngx-translate/core';
@@ -237,11 +237,6 @@ class CoreConstantsService {
237
237
  * Guarda la variable window del web browser.
238
238
  */
239
239
  window;
240
- /**
241
- * Tipo de ruta actual (ej: 'categories', 'attributes', etc).
242
- * Lo usamos para que PaginationService sepa qué filtro esperar.
243
- */
244
- currentRouteType = null;
245
240
  constructor() {
246
241
  if (isPlatformBrowser(this.platformId)) {
247
242
  this.window = window;
@@ -519,12 +514,6 @@ class CoreConstantsService {
519
514
  return this.apiConstants.CHANNEL;
520
515
  }
521
516
  form_sender = false;
522
- setCurrentRouteType(type) {
523
- this.currentRouteType = type;
524
- }
525
- getCurrentRouteType() {
526
- return this.currentRouteType;
527
- }
528
517
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: CoreConstantsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
529
518
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: CoreConstantsService, providedIn: 'root' });
530
519
  }
@@ -2738,24 +2727,17 @@ class Filter {
2738
2727
  };
2739
2728
  }
2740
2729
 
2741
- /**
2742
- * CategoryFilter
2743
- * - Gestiona el filtro de categorías.
2744
- * - Mantiene la estructura de categorías (parents/children) y permite:
2745
- * * Inicializar desde OptionsService.
2746
- * * Marcar elementos según slug o query param.
2747
- * * Generar porción de query string (&category=...).
2748
- */
2749
2730
  class CategoryFilter extends Filter {
2731
+ initialValues;
2750
2732
  _optionsService = inject(OptionsService);
2751
- _filtersService = inject(FiltersService);
2752
2733
  data = [];
2753
2734
  multi = false;
2754
- constructor(initialValue) {
2735
+ constructor(initialValues) {
2755
2736
  super();
2737
+ this.initialValues = initialValues;
2756
2738
  this._optionsService.getCategories().subscribe(res => {
2757
- this.setContent(res, initialValue);
2758
- this._filtersService.refreshFilters();
2739
+ //console.log(res);
2740
+ this.setContent(res, initialValues);
2759
2741
  });
2760
2742
  }
2761
2743
  type() {
@@ -2767,122 +2749,44 @@ class CategoryFilter extends Filter {
2767
2749
  this.data = categories;
2768
2750
  }
2769
2751
  getContent() {
2770
- return this.data;
2752
+ throw new Error("Method not implemented.");
2771
2753
  }
2772
- /**
2773
- * toUrlParams
2774
- * - Recorre la estructura y devuelve la porción de query string para la categoría seleccionada.
2775
- * - Prioriza el primer selected encontrado (solo una categoría se envía).
2776
- */
2777
- toUrlParams() {
2778
- const selectedCodes = [];
2779
- const walk = (nodes) => {
2780
- nodes.forEach(n => {
2781
- if (n.selected) {
2782
- selectedCodes.push(n.code || n.value);
2783
- }
2784
- if (Array.isArray(n.children) && n.children.length) {
2785
- walk(n.children);
2786
- }
2787
- });
2788
- };
2789
- walk(this.data);
2790
- if (selectedCodes.length > 0) {
2791
- return `&category=${encodeURIComponent(selectedCodes[0])}`;
2792
- }
2793
- return '';
2754
+ toUrlParams(actual_url = '', sublist) {
2755
+ let elements = sublist || this.data;
2756
+ elements.forEach((element) => {
2757
+ if (element.type == 'sub' && !element.selected) {
2758
+ const result = this.toUrlParams('', element.children);
2759
+ !actual_url.includes(result) ? actual_url += result : null;
2760
+ }
2761
+ else if (element.selected) {
2762
+ if (actual_url == '')
2763
+ actual_url = '&category=' + element.value;
2764
+ else
2765
+ actual_url = element.value;
2766
+ }
2767
+ });
2768
+ return actual_url;
2794
2769
  }
2795
2770
  createElement(filter, original, initialValue) {
2796
2771
  if (filter.type == 'sub') {
2797
2772
  filter.multi = false;
2798
2773
  filter.shape = 'text';
2799
2774
  filter.value = original.code;
2800
- filter.selected = (initialValue &&
2801
- this.removeAccents(original.slug?.toLowerCase()) ===
2802
- this.removeAccents(String(initialValue).toLowerCase()));
2775
+ filter.selected = (initialValue && this.removeAccents(original.slug?.toLowerCase()) == this.removeAccents(initialValue?.toLowerCase()));
2803
2776
  filter.children = filter.children.map((child, i) => this.createElement(child, original.children[i], initialValue));
2804
2777
  }
2805
2778
  else {
2806
2779
  filter.value = original.code;
2807
- filter.selected = (initialValue &&
2808
- this.removeAccents(original.slug?.toLowerCase()) ===
2809
- this.removeAccents(String(initialValue).toLowerCase()));
2780
+ filter.selected = (initialValue && this.removeAccents(original.slug?.toLowerCase()) == this.removeAccents(initialValue?.toLowerCase()));
2810
2781
  }
2811
2782
  return filter;
2812
2783
  }
2813
- /**
2814
- * setFromSlug
2815
- * - Marca como selected los nodos cuyo slug/path/name coincide con el slug proporcionado.
2816
- * - Utiliza removeAccents para comparar sin tildes y en minúsculas.
2817
- * - No retorna valor, solo modifica this.data.
2818
- */
2819
- setFromSlug(slug) {
2820
- const target = this.removeAccents(String(slug).toLowerCase());
2821
- const walk = (nodes) => {
2822
- nodes.forEach(n => {
2823
- // ADDED: iniciar sin selección explícita (asegura estado consistente)
2824
- n.selected = false;
2825
- const candidates = [
2826
- n.slug,
2827
- n.path && String(n.path).split('/').filter(Boolean).pop(),
2828
- n.name
2829
- ]
2830
- .filter(Boolean)
2831
- .map((s) => this.removeAccents(s.toLowerCase()));
2832
- if (candidates.includes(target)) {
2833
- n.selected = true;
2834
- }
2835
- if (Array.isArray(n.children) && n.children.length) {
2836
- walk(n.children);
2837
- }
2838
- });
2839
- };
2840
- walk(this.data);
2841
- }
2842
- /**
2843
- * hydrateFromQuery
2844
- * - Marca la categoría según el parámetro de query 'category' si existe.
2845
- * - Busca el code exacto en la estructura y marca el nodo correspondiente.
2846
- * - Loggea si se encontró o no la categoría.
2847
- */
2848
- hydrateFromQuery(query) {
2849
- const raw = query?.['category'];
2850
- if (!raw)
2851
- return;
2852
- const code = String(raw).trim();
2853
- if (!code)
2854
- return;
2855
- const mark = (nodes) => {
2856
- let found = false;
2857
- nodes.forEach(n => {
2858
- if (n.code === code) {
2859
- n.selected = true;
2860
- found = true;
2861
- }
2862
- else {
2863
- n.selected = false;
2864
- }
2865
- if (Array.isArray(n.children) && n.children.length) {
2866
- if (mark(n.children))
2867
- found = true;
2868
- }
2869
- });
2870
- return found;
2871
- };
2872
- const ok = mark(this.data);
2873
- }
2874
2784
  }
2875
2785
 
2876
- /**
2877
- * AttributesFilter
2878
- * - Gestiona el filtro de atributos (estructura de padres/children).
2879
- * - Soporta hidratación desde query params y aplicación de códigos seleccionados.
2880
- */
2881
2786
  class AttributesFilter extends Filter {
2882
2787
  _optionsService = inject(OptionsService);
2883
2788
  data = [];
2884
2789
  multi = false;
2885
- _codesFromQuery = null;
2886
2790
  constructor(initialValue, options) {
2887
2791
  super();
2888
2792
  if (options) {
@@ -2890,6 +2794,7 @@ class AttributesFilter extends Filter {
2890
2794
  }
2891
2795
  else {
2892
2796
  this._optionsService.getAttributes().subscribe(res => {
2797
+ //console.log(res);
2893
2798
  this.setContent(res, initialValue);
2894
2799
  });
2895
2800
  }
@@ -2899,18 +2804,10 @@ class AttributesFilter extends Filter {
2899
2804
  const attributes = this._optionsService.generateMenu(options);
2900
2805
  attributes.forEach((filter) => filter = this.createElement(filter, options, initialValue));
2901
2806
  this.data = attributes;
2902
- // Si ya teníamos códigos de la URL guardados, aplicarlos ahora
2903
- if (this._codesFromQuery && this._codesFromQuery.length) {
2904
- // ADDED: Aplicar códigos guardados tras cargar la data.
2905
- this.applyCodes(this._codesFromQuery);
2906
- }
2907
2807
  }
2908
2808
  getContent() {
2909
2809
  return this.data;
2910
2810
  }
2911
- /**
2912
- * ADDED: toUrlParams construye la porción de query string que representa las selecciones de atributos.
2913
- */
2914
2811
  toUrlParams(actual_url = '&attributeCodes=', sublist, already) {
2915
2812
  let elements = sublist || this.data;
2916
2813
  let aux_url = '';
@@ -2932,14 +2829,12 @@ class AttributesFilter extends Filter {
2932
2829
  }
2933
2830
  }
2934
2831
  });
2832
+ //console.log(actual_url + aux_url);
2935
2833
  return actual_url + aux_url;
2936
2834
  }
2937
2835
  ;
2938
2836
  cleanResult(text) { return text.replace('&attributeCodes=', ''); }
2939
2837
  ;
2940
- /**
2941
- * ADDED: createElement transforma cada nodo original en FilterElement y marca selección inicial si corresponde.
2942
- */
2943
2838
  createElement(filter, original, initialValue) {
2944
2839
  if (filter.type == 'sub') {
2945
2840
  filter.multi = false;
@@ -2952,65 +2847,10 @@ class AttributesFilter extends Filter {
2952
2847
  else {
2953
2848
  filter.value = original.code;
2954
2849
  filter.multi = false;
2955
- filter.selected = (initialValue &&
2956
- this.removeAccents(original.slug?.toLowerCase()) === this.removeAccents(initialValue?.toLowerCase()));
2850
+ filter.selected = (initialValue && this.removeAccents(original.slug?.toLowerCase()) == this.removeAccents(initialValue?.toLowerCase()));
2957
2851
  }
2958
2852
  return filter;
2959
2853
  }
2960
- /** hidratar desde la URL */
2961
- hydrateFromQuery(params) {
2962
- const raw = params['attributeCodes'];
2963
- if (!raw) {
2964
- // nada en la URL → limpió filtro
2965
- this._codesFromQuery = null;
2966
- this.clearSelection();
2967
- return;
2968
- }
2969
- const codes = String(raw)
2970
- .split('<and>')
2971
- .map(c => c.trim())
2972
- .filter(Boolean);
2973
- if (!codes.length) {
2974
- this._codesFromQuery = null;
2975
- this.clearSelection();
2976
- return;
2977
- }
2978
- this._codesFromQuery = codes;
2979
- // Si ya tengo data cargada, aplico ahora;
2980
- // si no, se aplicará en setContent().
2981
- if (this.data && this.data.length) {
2982
- // ADDED: Aplicar códigos inmediatamente porque la data ya está disponible.
2983
- this.applyCodes(codes);
2984
- }
2985
- }
2986
- /**
2987
- * ADDED: applyCodes recorre la estructura y marca selected=true en los children cuyo código está en 'codes'.
2988
- * Además marca el parent como seleccionado si alguno de sus hijos lo está.
2989
- */
2990
- applyCodes(codes) {
2991
- this.data?.forEach((parent) => {
2992
- parent.selected = false;
2993
- (parent.children || []).forEach((child) => {
2994
- const code = child.code || child.value;
2995
- const isSelected = codes.includes(code);
2996
- child.selected = isSelected;
2997
- if (isSelected) {
2998
- parent.selected = true;
2999
- }
3000
- });
3001
- });
3002
- }
3003
- /**
3004
- * ADDED: clearSelection desmarca todos los parents y children (restablece el filtro).
3005
- */
3006
- clearSelection() {
3007
- this.data?.forEach((parent) => {
3008
- parent.selected = false;
3009
- (parent.children || []).forEach((child) => {
3010
- child.selected = false;
3011
- });
3012
- });
3013
- }
3014
2854
  }
3015
2855
 
3016
2856
  class DynamicsFilter extends Filter {
@@ -3171,8 +3011,8 @@ class PriceRangeFilter extends Filter {
3171
3011
  if (options.minPrice != null && options.maxPrice != null) {
3172
3012
  this.minPrice = options.minPrice;
3173
3013
  this.maxPrice = options.maxPrice;
3174
- this.currentMinPrice = options.currentMinPrice ?? null;
3175
- this.currentMaxPrice = options.currentMaxPrice ?? null;
3014
+ this.currentMinPrice = options.currentMinPrice ?? this.minPrice;
3015
+ this.currentMaxPrice = options.currentMaxPrice ?? this.maxPrice;
3176
3016
  this._loaded = true;
3177
3017
  }
3178
3018
  };
@@ -3217,13 +3057,9 @@ class FilterFactory {
3217
3057
  create(filterType, productsFilter) {
3218
3058
  switch (filterType) {
3219
3059
  case 'categories':
3220
- return new CategoryFilter(productsFilter?.type === 'categories'
3221
- ? productsFilter.value
3222
- : null);
3060
+ return new CategoryFilter(productsFilter ? (productsFilter.type == 'categories' && productsFilter.value) : null);
3223
3061
  case 'attributes':
3224
- return new AttributesFilter(productsFilter?.type === 'attributes'
3225
- ? productsFilter.value
3226
- : null);
3062
+ return new AttributesFilter(productsFilter ? (productsFilter.type == 'attributes' && productsFilter.value) : null);
3227
3063
  case 'dynamics':
3228
3064
  return new DynamicsFilter();
3229
3065
  case 'sort':
@@ -3835,7 +3671,6 @@ class FiltersService {
3835
3671
  limit: 0
3836
3672
  };
3837
3673
  _filtersInitialized = false;
3838
- _lastRouteQuery = null;
3839
3674
  constructor() {
3840
3675
  //this._defaultFilters = this._consts.getDefaultFilters()
3841
3676
  }
@@ -3856,20 +3691,10 @@ class FiltersService {
3856
3691
  let extra_params = '';
3857
3692
  this._filtersSubject.value.forEach(filter => {
3858
3693
  const extra = filter.toUrlParams();
3859
- if (extra && extra.split('=')[1] !== '') {
3860
- extra_params += extra;
3861
- }
3694
+ extra.split('=')[1] != '' ? extra_params += extra : null;
3862
3695
  });
3863
- // Si en la URL ya venía `attributeCodes` pero aún ningún filtro lo agregó,
3864
- // lo volvemos a sumar a mano. Esto evita que al refrescar (SSR) se pierda el filtro
3865
- // aunque el AttributesFilter todavía no haya terminado de hidratarse.
3866
- const attributeCodesFromRoute = this._lastRouteQuery?.['attributeCodes'];
3867
- if (attributeCodesFromRoute && !extra_params.includes('attributeCodes=')) {
3868
- extra_params += `&attributeCodes=${attributeCodesFromRoute}`;
3869
- }
3870
- if (search_value) {
3871
- extra_params += '&criteria[search][type]=contains&criteria[search][value]=' + search_value;
3872
- }
3696
+ if (search_value)
3697
+ extra_params += ('&criteria[search][type]=contains&criteria[search][value]=' + search_value);
3873
3698
  return this.productsFilterApi(extra_params);
3874
3699
  }
3875
3700
  isUpdated(paginationSettings) {
@@ -3885,7 +3710,7 @@ class FiltersService {
3885
3710
  this._paginationSettings = paginationSettings;
3886
3711
  return change;
3887
3712
  }
3888
- setFilters(paginationSettings, search_value, routeQueryParams) {
3713
+ setFilters(paginationSettings, search_value) {
3889
3714
  this._paginationSettings = paginationSettings;
3890
3715
  let final_filters = [];
3891
3716
  let filtersToProcess = this._optionsFilters?.includes('all')
@@ -3898,30 +3723,15 @@ class FiltersService {
3898
3723
  if (filter) {
3899
3724
  final_filters.push(filter);
3900
3725
  }
3726
+ else {
3727
+ // console.warn(`❌ Failed to create filter for type: ${filterType}`);
3728
+ }
3901
3729
  });
3902
3730
  });
3903
3731
  this._defaultFilters?.forEach(filterDefault => {
3904
3732
  let filter = final_filters.find(filter => filter.type() == filterDefault.filter_type);
3905
3733
  filter && filterDefault.codes.forEach(value => filter.setSelected(value));
3906
3734
  });
3907
- // hidratar price_range desde la URL
3908
- if (routeQueryParams) {
3909
- const pr = final_filters.find(f => f.type() === 'price_range');
3910
- if (pr) {
3911
- const rawMin = routeQueryParams['price_min'];
3912
- const rawMax = routeQueryParams['price_max'];
3913
- const min = rawMin != null ? Number(rawMin) : null;
3914
- const max = rawMax != null ? Number(rawMax) : null;
3915
- if ((min != null && !Number.isNaN(min)) || (max != null && !Number.isNaN(max))) {
3916
- if (min != null && !Number.isNaN(min)) {
3917
- pr.currentMinPrice = min;
3918
- }
3919
- if (max != null && !Number.isNaN(max)) {
3920
- pr.currentMaxPrice = max;
3921
- }
3922
- }
3923
- }
3924
- }
3925
3735
  this._filtersSubject.next(final_filters);
3926
3736
  }
3927
3737
  getFilters(paginationSettings) {
@@ -3936,7 +3746,9 @@ class FiltersService {
3936
3746
  runInInjectionContext(this.environmentInjector, () => {
3937
3747
  const filterFactory = new FilterFactory();
3938
3748
  filtersToProcess?.forEach(filterType => {
3749
+ // console.log('Creating filter for type:', filterType);
3939
3750
  const filter = filterFactory.create(filterType, settings);
3751
+ // console.log('Created filter:', filter);
3940
3752
  if (filter) {
3941
3753
  final_filters.push(filter);
3942
3754
  }
@@ -3961,6 +3773,7 @@ class FiltersService {
3961
3773
  if (filterObj.type() !== 'price_range') {
3962
3774
  let index = final_filters.findIndex(filter => filter.type() == filterObj.type());
3963
3775
  final_filters[index].setSelected(filterElem, filterElem.value || filterElem.code);
3776
+ console.log(index, final_filters);
3964
3777
  }
3965
3778
  this._filtersSubject.next(final_filters);
3966
3779
  // }
@@ -3970,6 +3783,13 @@ class FiltersService {
3970
3783
  runInInjectionContext(this.environmentInjector, () => {
3971
3784
  const filterFactory = new FilterFactory();
3972
3785
  const filter = [];
3786
+ /* claves.forEach((key:any) => {
3787
+ filter.push(filterFactory.create(key))
3788
+ paginationFilters[key].forEach((value:any) => {
3789
+ console.log(filter[0].createElement(value,paginationFilters[key]));
3790
+ })
3791
+
3792
+ }) */
3973
3793
  this._filtersSubject.value.forEach((value) => {
3974
3794
  if (value.type() == "attributes") {
3975
3795
  //value.data = [];
@@ -3989,95 +3809,6 @@ class FiltersService {
3989
3809
  pr.setSelected(min, max);
3990
3810
  this._filtersSubject.next([...filters]);
3991
3811
  }
3992
- /**
3993
- * Fuerza a emitir de nuevo los filtros actuales.
3994
- * Útil cuando un filtro async (categorías, atributos) termina de cargarse
3995
- * y necesitamos que PaginationService vuelva a armar la URL.
3996
- */
3997
- refreshFilters() {
3998
- const current = this._filtersSubject.value;
3999
- // Emitimos una copia para que los subscribers detecten el cambio
4000
- this._filtersSubject.next([...current]);
4001
- }
4002
- /**
4003
- * Punto central donde se hidratan los filtros a partir de la URL:
4004
- * - Lee type/value de la ruta (ej. /collection/categories/:value).
4005
- * - Lee query params (category, price_min, price_max, attributeCodes, search, etc.).
4006
- * - Crea instancias de filtros y les delega la hidratación específica.
4007
- *
4008
- * Este método se llama tanto en SSR como en navegador.
4009
- */
4010
- hydrateFromRoute(paginationSettings, routeParams, routeQuery) {
4011
- // hidratar search desde la URL ===
4012
- const searchFromUrl = routeQuery?.['search'];
4013
- if (typeof searchFromUrl === 'string' && searchFromUrl.trim() !== '') {
4014
- // Guardamos el valor para que otros componentes (Header, Collection) lo reutilicen
4015
- this._consts.searchValue = searchFromUrl.trim();
4016
- }
4017
- else {
4018
- // Si no viene `search` en la URL, limpiamos el estado global para no arrastrar búsquedas viejas
4019
- this._consts.searchValue = '';
4020
- }
4021
- // Guardamos la última query completa para usarla en generateFinalApi (parche attributeCodes)
4022
- this._lastRouteQuery = routeQuery;
4023
- // Guardamos las settings actuales (type, value, limit, etc.)
4024
- this._paginationSettings = paginationSettings;
4025
- // Creamos los filtros base (attributes, categories, dynamics, sort, price_range, etc.)
4026
- let final_filters = [];
4027
- let filtersToProcess = this._optionsFilters?.includes('all')
4028
- ? ['attributes', 'categories', 'dynamics', 'sort', 'price_range']
4029
- : this._optionsFilters;
4030
- runInInjectionContext(this.environmentInjector, () => {
4031
- const filterFactory = new FilterFactory();
4032
- filtersToProcess?.forEach(filterType => {
4033
- const filter = filterFactory.create(filterType, paginationSettings);
4034
- if (filter) {
4035
- final_filters.push(filter);
4036
- }
4037
- });
4038
- });
4039
- // Aplicamos defaultFilters si corresponde
4040
- this._defaultFilters?.forEach(filterDefault => {
4041
- const filter = final_filters.find(f => f.type() === filterDefault.filter_type);
4042
- filter && filterDefault.codes.forEach(value => filter.setSelected(value));
4043
- });
4044
- // Hidratar filtros con slug y query params genéricos
4045
- final_filters.forEach(f => {
4046
- const anyFilter = f;
4047
- // type/value en la URL: /collection/categories/:value
4048
- const urlType = routeParams['type'];
4049
- const urlValue = routeParams['value'];
4050
- // Por ejemplo, el CategoryFilter implementa setFromSlug para marcar la categoría correcta
4051
- if (urlType === 'categories' && urlValue && typeof anyFilter.setFromSlug === 'function') {
4052
- anyFilter.setFromSlug(urlValue);
4053
- }
4054
- // query params genéricos: ?category=0101&price_min=...&price_max=...&attributeCodes=...
4055
- // Cada filtro que lo soporte implementa hydrateFromQuery y decide qué leer.
4056
- if (typeof anyFilter.hydrateFromQuery === 'function') {
4057
- anyFilter.hydrateFromQuery(routeQuery);
4058
- }
4059
- });
4060
- // Hidratar específicamente el PriceRangeFilter desde la URL
4061
- const priceFilter = final_filters.find((f) => f instanceof PriceRangeFilter);
4062
- if (priceFilter && routeQuery) {
4063
- const minFromUrl = routeQuery['price_min'];
4064
- const maxFromUrl = routeQuery['price_max'];
4065
- if (minFromUrl != null || maxFromUrl != null) {
4066
- const min = minFromUrl != null ? Number(minFromUrl) : priceFilter.minPrice;
4067
- const max = maxFromUrl != null ? Number(maxFromUrl) : priceFilter.maxPrice;
4068
- // usamos la API del filtro para setear el rango
4069
- priceFilter.setSelected(min, max);
4070
- }
4071
- else {
4072
- // si no hay nada en la URL, lo reseteamos
4073
- priceFilter.reset();
4074
- }
4075
- }
4076
- // Emitimos filtros ya hidratados para que PaginationService y Collection reaccionen
4077
- this._filtersSubject.next(final_filters);
4078
- // Marcamos que el sistema de filtros está listo (útil para mostrar spinners, etc.)
4079
- this._readySubject.next(true);
4080
- }
4081
3812
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: FiltersService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
4082
3813
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: FiltersService, providedIn: 'root' });
4083
3814
  }
@@ -4089,55 +3820,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
4089
3820
  }], ctorParameters: () => [] });
4090
3821
 
4091
3822
  /**
4092
- * Servicio para manejar la paginación y la carga de productos.
4093
- *
4094
- * Se encarga de:
4095
- * - Escuchar los cambios en los filtros (categorías, atributos, precio, búsqueda, etc.).
4096
- * - Construir la URL al backend con esos filtros.
4097
- * - Pedir los productos al endpoint de product-search.
4098
- * - Exponer un observable con la última página de productos (`paginationData$`)
4099
- * y otros helpers (precio mínimo/máximo, siguiente página, etc.).
3823
+ * Servicio para manejar la paginación de los productos.
3824
+ * @class PaginationService
4100
3825
  */
4101
3826
  class PaginationService {
4102
3827
  _connectionService = inject(ConnectionService);
4103
3828
  _filtersService = inject(FiltersService);
4104
3829
  _constants = inject(CoreConstantsService);
4105
- /**
4106
- * Flujo principal: a partir de los filtros → arma URL → consulta backend → devuelve productos.
4107
- *
4108
- * Pasos:
4109
- * 1) Espera a que `FiltersService` emita filtros válidos.
4110
- * 2) Construye la URL final con `buildUrl()`.
4111
- * 3) Evita repetir llamadas si la URL no cambió (`distinctUntilChanged`).
4112
- * 4) Llama al backend con `getData(url)`.
4113
- * 5) Comparte el último resultado con todos los suscriptores (`shareReplay(1)`).
4114
- */
4115
- paginationData$ = this._filtersService.filters$.pipe(tap(filters => {
4116
- }), filter(filters => {
4117
- if (!filters || !filters.length)
4118
- return false;
4119
- const categoryFilter = filters.find(f => f.type() === 'categories');
4120
- const hasCategorySelected = !!categoryFilter?.getSelectedList()?.length;
4121
- const isCategoryRoute = this._constants.currentRouteType === 'categories';
4122
- // Si estamos en ruta de categorías y todavía no se marcó ninguna,
4123
- // esperamos a que el CategoryFilter se hidrate antes de disparar la llamada.
4124
- if (isCategoryRoute && !hasCategorySelected) {
4125
- return false;
4126
- }
4127
- return true;
4128
- }),
4129
- // 2) Convertimos filtros -> URL
4130
- map(filters => {
3830
+ paginationData$ = this._filtersService.filters$.pipe(shareReplay(1), filter(filters => filters && filters.length > 0), switchMap((filters) => {
4131
3831
  const url = this.buildUrl(filters);
4132
- return url;
4133
- }),
4134
- // 3) Solo seguimos si la URL cambió respecto de la emisión anterior
4135
- distinctUntilChanged(),
4136
- // 4) Llamamos al backend con la URL construida
4137
- tap(url => {
4138
- }), switchMap(url => this.getData(url)),
4139
- // 5) Reutilizar resultado si alguien más se suscribe (evita repetir la petición)
4140
- shareReplay(1));
3832
+ return this.getData(url);
3833
+ }));
4141
3834
  _dataPagination = signal({
4142
3835
  attributes: [],
4143
3836
  category: [],
@@ -4206,8 +3899,7 @@ class PaginationService {
4206
3899
  this._dataPagination.set({ ...response, called: true });
4207
3900
  this._finished = (response.page == response.pages);
4208
3901
  this._waiting = false;
4209
- // Emitir los valores de price_min y price_max a través de priceRangeSubject,
4210
- // para que otros componentes (ej. filtro de precio) puedan mostrarlos.
3902
+ // Emitir los valores de price_min y price_max a través de priceRangeSubject
4211
3903
  this.priceRangeSubject.next({
4212
3904
  price_min: response.price_min,
4213
3905
  price_max: response.price_max
@@ -4267,36 +3959,27 @@ class PaginationService {
4267
3959
  * @returns
4268
3960
  */
4269
3961
  buildUrl(filters) {
4270
- const url = this._constants.searchValue
4271
- ? this._filtersService.generateFinalApi(this._constants.searchValue)
4272
- : this._filtersService.generateFinalApi();
4273
- return url;
3962
+ if (this._constants.searchValue) {
3963
+ return this._filtersService.generateFinalApi(this._constants.searchValue);
3964
+ }
3965
+ else {
3966
+ return this._filtersService.generateFinalApi();
3967
+ }
4274
3968
  }
4275
3969
  /**
4276
- * Devuelve un observable de los productos que devolvió la última página obtenida.
3970
+ * Devuelve un observable de los productos que devolvio la última página obtenida.
4277
3971
  * @param url
4278
3972
  * @returns
4279
3973
  */
4280
3974
  getData(url) {
4281
- // Detectar la página desde la URL (?page=n). Si no viene, asumimos page=1.
4282
- const pageMatch = url.match(/[?&]page=(\d+)/);
4283
- const page = pageMatch ? Number(pageMatch[1]) : 1;
4284
- // Si es la primera página, reseteamos estado interno
4285
- if (page === 1) {
4286
- this._resetSubject.next(true);
4287
- this._nextProductsSubject.next([]);
4288
- }
4289
- // Llamamos al backend con el page correcto
4290
- return this._connectionService.get(url, { limit: 10, page }).pipe(tap((res) => {
4291
- // Actualizar datos de paginación + price_min/price_max
3975
+ this._resetSubject.next(true);
3976
+ this._nextProductsSubject.next([]);
3977
+ this._connectionService.get(url, { limit: 10, page: 1 }).pipe(map(res => {
3978
+ //console.log(res)
3979
+ this._nextProductsSubject.next(res.items);
4292
3980
  res.links ? this.updatePageData(res) : this.finish(res);
4293
- }), map((res) => {
4294
- const items = res.items ?? [];
4295
- // Mantener nextProducts$ sincronizado (por compatibilidad)
4296
- this._nextProductsSubject.next(items);
4297
- // Lo que ve paginationData$ (switchMap) son estos items
4298
- return items;
4299
- }));
3981
+ })).subscribe();
3982
+ return this.nextProducts$;
4300
3983
  }
4301
3984
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: PaginationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
4302
3985
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: PaginationService, providedIn: 'root' });
@@ -4361,11 +4044,9 @@ class ProductsService {
4361
4044
  * @param paginationSettings
4362
4045
  * @param searchValue
4363
4046
  */
4364
- getProductsForFilter(paginationSettings, searchValue, routeQueryParams) {
4047
+ getProductsForFilter(paginationSettings, searchValue) {
4365
4048
  searchValue ? this.searchValue.set(searchValue) : this.searchValue.set('');
4366
- if (paginationSettings) {
4367
- this._filtersService.setFilters(paginationSettings, searchValue, routeQueryParams);
4368
- }
4049
+ paginationSettings && this._filtersService.setFilters(paginationSettings);
4369
4050
  }
4370
4051
  /**
4371
4052
  * Actualiza los productos con los de la siguiente pagina.
@@ -6490,8 +6171,10 @@ class HeaderEcComponent extends MenuEcComponent {
6490
6171
  constructor() {
6491
6172
  super();
6492
6173
  this._channelService.channel$.subscribe(cfg => {
6174
+ // console.log('Channel configuration:', cfg);
6493
6175
  this.showPricesOnlyToLoggedUsers = !!cfg.showPricesOnlyToLoggedUsers;
6494
6176
  this.hidePrices = !!cfg.hidePrices;
6177
+ // console.log('hidePrices:', this.hidePrices);
6495
6178
  });
6496
6179
  }
6497
6180
  coreConstantsService = inject(CoreConstantsService);
@@ -6499,7 +6182,6 @@ class HeaderEcComponent extends MenuEcComponent {
6499
6182
  cdr = inject(ChangeDetectorRef); // Inyectamos ChangeDetectorRef para forzar la actualización
6500
6183
  ngOnInit() {
6501
6184
  this.channel = this.coreConstantsService.getChannel();
6502
- this.searchValue = this.coreConstantsService.searchValue || '';
6503
6185
  this.onWindowScroll();
6504
6186
  this.detectRouteChange(); // Llamamos a la función que detecta el cambio de ruta
6505
6187
  // Usar el Observable del AuthService
@@ -6587,19 +6269,10 @@ class HeaderEcComponent extends MenuEcComponent {
6587
6269
  }
6588
6270
  this.searchValue = '';
6589
6271
  this.coreConstantsService.searchValue = '';
6590
- // En lugar de relanzar la búsqueda con string vacío,
6591
- // actualizamos la URL para quitar el query param `search` y resetear la paginación.
6592
- this.router.navigate(['/collection'], {
6593
- queryParams: {
6594
- search: null,
6595
- page: null
6596
- },
6597
- queryParamsHandling: 'merge'
6598
- });
6272
+ this.getCollectionSearch();
6599
6273
  }
6600
6274
  setupMobileMenu() {
6601
- if (!isPlatformBrowser(this.platformId) || typeof document === 'undefined')
6602
- return;
6275
+ // console.log('setupMobileMenu called');
6603
6276
  const menuMobile = document.querySelector('.menuMobile');
6604
6277
  if (!(menuMobile instanceof HTMLElement))
6605
6278
  return;
@@ -8145,16 +7818,26 @@ class RedsysCatchEcComponent extends ComponentHelper {
8145
7818
  }
8146
7819
  /** Intenta cerrar la pestaña actual; si falla, ejecuta el callback de fallback. */
8147
7820
  tryCloseSelf(onFail) {
8148
- if (!isPlatformBrowser(this.platformId))
8149
- return onFail();
8150
- let attempted = false;
7821
+ if (!isPlatformBrowser(this.platformId)) {
7822
+ onFail();
7823
+ return;
7824
+ }
7825
+ // Fallback de seguridad: si después de ~800 ms la página sigue visible,
7826
+ // asumimos que window.close() fue ignorado.
7827
+ const fallback = setTimeout(() => {
7828
+ // Si la pestaña siguiera abierta, document.hidden suele ser false.
7829
+ if (!document.hidden) {
7830
+ onFail();
7831
+ }
7832
+ }, 800);
8151
7833
  try {
8152
7834
  window.close();
8153
- attempted = true;
7835
+ // Si window.close() lanza excepción, limpiamos el timeout y hacemos fallback inmediato.
8154
7836
  }
8155
- catch { }
8156
- if (!attempted)
7837
+ catch {
7838
+ clearTimeout(fallback);
8157
7839
  onFail();
7840
+ }
8158
7841
  }
8159
7842
  /** Oculta header/footer para esta pantalla mínima. */
8160
7843
  hideHeaderFooter() {
@@ -8383,7 +8066,6 @@ class CollectionEcComponent {
8383
8066
  _productsService = inject(ProductsService);
8384
8067
  _activeRoute = inject(ActivatedRoute);
8385
8068
  _optionsService = inject(OptionsService);
8386
- _filtersService = inject(FiltersService);
8387
8069
  params$ = this._activeRoute.params;
8388
8070
  //public ready = this._optionsService.ready
8389
8071
  queryParams$ = this._activeRoute.queryParams;
@@ -8395,63 +8077,40 @@ class CollectionEcComponent {
8395
8077
  defaultFilters = [];
8396
8078
  loading = false;
8397
8079
  countProducts = signal(0);
8398
- loaded = signal(false);
8080
+ loaded = false;
8399
8081
  optionsFilters = ['all'];
8400
8082
  filters_sort = [];
8083
+ _filtersService = inject(FiltersService);
8401
8084
  filters$ = this._filtersService.filters$;
8402
8085
  ready$ = this._filtersService.ready$;
8403
8086
  window;
8404
8087
  isList = false;
8405
- destroy$ = new Subject();
8406
8088
  setAsList = (value) => this.isList = value;
8407
8089
  ngOnInit() {
8408
- if (isPlatformBrowser(this.platformId)) {
8409
- this.window?.scroll(0, 0);
8410
- }
8411
- combineLatest([this.params$, this.queryParams$])
8412
- .pipe(map(([params, query]) => ({ params, query })), distinctUntilChanged((a, b) => JSON.stringify(a) === JSON.stringify(b)), takeUntil(this.destroy$))
8413
- .subscribe(({ params, query }) => {
8414
- // Guardamos el tipo de ruta actual en las constantes (ej: 'categories', 'sections', etc.)
8415
- const routeType = params['type'] || null;
8416
- this.constanst.setCurrentRouteType(routeType);
8417
- const paginationSettings = {
8418
- latest: true,
8419
- limit: 10,
8420
- type: params['type'] || null,
8421
- value: params['value'] || null
8422
- };
8423
- // Punto clave:
8424
- // A partir de la URL (params + query) reconstruimos los filtros
8425
- // (categorías, atributos, rango de precios, etc.)
8426
- // Esto permite:
8427
- // - Soportar F5 / recarga sin perder filtros
8428
- // - Navegar con URL compartibles (deep linking)
8429
- this._filtersService.hydrateFromRoute(paginationSettings, params, query);
8430
- });
8090
+ this.getProducts();
8091
+ this.window?.scroll(0, 0);
8431
8092
  }
8432
8093
  //protected readonly questions = signal<Question[]>([]);
8433
8094
  total = 0;
8434
8095
  platformId = inject(PLATFORM_ID);
8435
8096
  constructor() {
8436
- // Guardamos window sólo en Browser para evitar errores en SSR
8437
8097
  if (isPlatformBrowser(this.platformId)) {
8438
8098
  this.window = window;
8439
8099
  }
8440
- // Nos suscribimos al stream de productos.
8441
- // Cuando ProductsService emite, marcamos `loaded` en true, pero solo en Browser.
8442
- // En SSR no lo marcamos para evitar cambios de estado innecesarios.
8443
- this.products$
8444
- .pipe(takeUntil(this.destroy$))
8445
- .subscribe(products => {
8446
- if (isPlatformBrowser(this.platformId)) {
8447
- this.loaded.set(true);
8100
+ }
8101
+ getProducts() {
8102
+ combineLatest([this.params$, this.queryParams$]).subscribe({
8103
+ next: ([params, queryParams]) => {
8104
+ const paginationSettings = {
8105
+ latest: true,
8106
+ limit: 10,
8107
+ type: params['type'] || null,
8108
+ value: params['value'] || null
8109
+ };
8110
+ this._productsService.getProductsForFilter(paginationSettings, queryParams["search"]);
8448
8111
  }
8449
8112
  });
8450
8113
  }
8451
- ngOnDestroy() {
8452
- this.destroy$.next();
8453
- this.destroy$.complete();
8454
- }
8455
8114
  onScroll() {
8456
8115
  this.loading = true;
8457
8116
  this._productsService.updateProducts();
@@ -9079,7 +8738,6 @@ class FiltersEcComponent {
9079
8738
  injector = inject(Injector);
9080
8739
  isAuthenticated$ = this._authService.isAuthenticated();
9081
8740
  hidePrices = false;
9082
- route = inject(ActivatedRoute);
9083
8741
  setSelect;
9084
8742
  ngOnInit() {
9085
8743
  }
@@ -9109,56 +8767,26 @@ class FiltersEcComponent {
9109
8767
  .pop()
9110
8768
  selectedOption && this._filtersService.setFilterSelected(this.filters[0], selectedOption) */
9111
8769
  }
9112
- /**
9113
- * Maneja el click sobre un elemento de filtro (categoría, atributo, etc.).
9114
- *
9115
- * - Para categorías: navega a la URL de la categoría.
9116
- * - Para atributos: actualiza la query string de la URL con `attributeCodes`.
9117
- * - Para otros filtros: delega en FiltersService para marcar seleccionado.
9118
- *
9119
- * La idea es que **la URL siempre represente los filtros aplicados**,
9120
- * de modo que:
9121
- * - al hacer F5 no se pierdan los filtros,
9122
- * - los links sean compartibles (deep linking).
9123
- */
9124
8770
  setSelected(filter, selected) {
9125
8771
  if (!filter || !selected) {
8772
+ console.error('Filter or selected element is undefined:', { filter, selected });
9126
8773
  return;
9127
8774
  }
9128
- // Si el elemento está marcado como no visible, no hacemos nada
9129
- if (selected.isVisible === false)
9130
- return;
9131
8775
  if (typeof filter.setSelected !== 'function') {
8776
+ console.error('filter.setSelected is not a function. Filter might not be an instance of the expected class:', filter);
9132
8777
  return;
9133
8778
  }
9134
8779
  try {
8780
+ // this._filtersService.setFilterSelected(filter, selected);
9135
8781
  if (filter.type() === 'categories') {
9136
- // Para categorías usamos navegación por path (ej: /collection/categories/camas-elasticas)
9137
8782
  if (selected.path) {
9138
8783
  this.router.navigate([selected.path]);
9139
8784
  }
9140
- return;
9141
8785
  }
9142
- if (filter.type() === 'attributes') {
9143
- // El backend espera que le mandemos el "code" del atributo en el query param
9144
- const code = selected.code || selected.value || null;
9145
- // Actualizamos la URL manteniendo la ruta, pero agregando `attributeCodes`
9146
- // Ejemplo de resultado:
9147
- // /collection/categories/camas-elasticas?attributeCodes=ABC123&...
9148
- //
9149
- // También reseteamos la página a null para que vuelva a la primera página
9150
- // cuando se aplica un nuevo filtro.
9151
- this.router.navigate([], {
9152
- relativeTo: this.route,
9153
- queryParams: {
9154
- attributeCodes: code,
9155
- page: null
9156
- },
9157
- queryParamsHandling: 'merge'
9158
- });
9159
- return;
8786
+ else if (filter.type() === 'attributes') {
8787
+ // Manejar la navegación para atributos
8788
+ this._filtersService.setFilterSelected(filter, selected);
9160
8789
  }
9161
- this._filtersService.setFilterSelected(filter, selected);
9162
8790
  }
9163
8791
  catch (error) {
9164
8792
  console.error("Error while setting selected filter:", error);
@@ -9192,9 +8820,7 @@ class FiltersEcComponent {
9192
8820
  return true;
9193
8821
  };
9194
8822
  scrollUp = () => {
9195
- if (typeof window !== 'undefined') {
9196
- window.scroll(0, 0);
9197
- }
8823
+ window.scroll(0, 0);
9198
8824
  return true;
9199
8825
  };
9200
8826
  hasAppliedFilters() {
@@ -10208,6 +9834,7 @@ class MpRedirectEcComponent {
10208
9834
  _toastService = inject(ToastService);
10209
9835
  platformId = inject(PLATFORM_ID);
10210
9836
  finished = false;
9837
+ ngZone = inject(NgZone);
10211
9838
  method = null;
10212
9839
  total_amount = 0;
10213
9840
  allData;
@@ -10230,10 +9857,10 @@ class MpRedirectEcComponent {
10230
9857
  this.windowRef = window;
10231
9858
  if ('BroadcastChannel' in window) {
10232
9859
  this.bc = new BroadcastChannel('mp_payment');
10233
- this.bc.onmessage = (e) => this.onMpMessage(e?.data);
9860
+ this.bc.onmessage = (e) => this.ngZone.run(() => this.onMpMessage(e?.data));
10234
9861
  }
10235
- window.addEventListener('storage', this.onStorage);
10236
- window.addEventListener('message', this.onWindowMessage);
9862
+ window.addEventListener('storage', (e) => this.ngZone.run(() => this.onStorage(e)));
9863
+ window.addEventListener('message', (e) => this.ngZone.run(() => this.onWindowMessage(e)));
10237
9864
  }
10238
9865
  this.getPreference();
10239
9866
  }
@@ -10286,23 +9913,29 @@ class MpRedirectEcComponent {
10286
9913
  }, 1000);
10287
9914
  };
10288
9915
  onWindowMessage = (event) => {
10289
- const data = event?.data;
10290
- this.onMpMessage(data);
9916
+ this.ngZone.run(() => {
9917
+ const data = event?.data;
9918
+ this.onMpMessage(data);
9919
+ });
10291
9920
  };
10292
9921
  onStorage = (e) => {
10293
- if (!e.key || !this.sid)
10294
- return;
10295
- if (e.key === `mp:state:${this.sid}` && e.newValue) {
10296
- const state = e.newValue;
10297
- this.finishWithState(state);
10298
- }
9922
+ this.ngZone.run(() => {
9923
+ if (!e.key || !this.sid)
9924
+ return;
9925
+ if (e.key === `mp:state:${this.sid}` && e.newValue) {
9926
+ const state = e.newValue;
9927
+ this.finishWithState(state);
9928
+ }
9929
+ });
10299
9930
  };
10300
9931
  onMpMessage = (data) => {
10301
- if (!data || data.type !== 'mp:state')
10302
- return;
10303
- if (data.sid !== this.sid)
10304
- return;
10305
- this.finishWithState(data.state);
9932
+ this.ngZone.run(() => {
9933
+ if (!data || data.type !== 'mp:state')
9934
+ return;
9935
+ if (data.sid !== this.sid)
9936
+ return;
9937
+ this.finishWithState(data.state);
9938
+ });
10306
9939
  };
10307
9940
  checkLocalStorageOnce() {
10308
9941
  if (!this.sid)
@@ -10313,32 +9946,34 @@ class MpRedirectEcComponent {
10313
9946
  }
10314
9947
  /** Cierra el flujo de pago con el estado final y notifica al padre. */
10315
9948
  finishWithState(state) {
10316
- if (this.finished)
10317
- return;
10318
- this.finished = true;
10319
- if (this.pollTimer)
10320
- clearInterval(this.pollTimer);
10321
- this.pollTimer = null;
10322
- localStorage.removeItem(`mp:state:${this.sid}`);
10323
- localStorage.removeItem('mp:sid');
10324
- localStorage.removeItem('state');
10325
- try {
10326
- this.ventana && !this.ventana.closed && this.ventana.close();
10327
- }
10328
- catch { }
10329
- this.ventana = null;
10330
- if (state === 'success' || state === 'pending') {
10331
- this.phase = 'finalizing';
10332
- this.ready.emit(true);
10333
- }
10334
- else if (state === 'failure' || state === 'cancel') {
10335
- this.phase = 'idle';
10336
- this._toastService.show(state === 'cancel' ? 'Se canceló el pago con Mercado Pago' : 'payment-error');
10337
- }
10338
- else {
10339
- this.phase = 'idle';
10340
- this._toastService.show('payment-error');
10341
- }
9949
+ this.ngZone.run(() => {
9950
+ if (this.finished)
9951
+ return;
9952
+ this.finished = true;
9953
+ if (this.pollTimer)
9954
+ clearInterval(this.pollTimer);
9955
+ this.pollTimer = null;
9956
+ localStorage.removeItem(`mp:state:${this.sid}`);
9957
+ localStorage.removeItem('mp:sid');
9958
+ localStorage.removeItem('state');
9959
+ try {
9960
+ this.ventana && !this.ventana.closed && this.ventana.close();
9961
+ }
9962
+ catch { }
9963
+ this.ventana = null;
9964
+ if (state === 'success' || state === 'pending') {
9965
+ this.phase = 'finalizing';
9966
+ this.ready.emit(true);
9967
+ }
9968
+ else if (state === 'failure' || state === 'cancel') {
9969
+ this.phase = 'idle';
9970
+ this._toastService.show(state === 'cancel' ? 'Se canceló el pago con Mercado Pago' : 'payment-error');
9971
+ }
9972
+ else {
9973
+ this.phase = 'idle';
9974
+ this._toastService.show('payment-error');
9975
+ }
9976
+ });
10342
9977
  }
10343
9978
  genSid() {
10344
9979
  return `mp_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
@@ -10704,6 +10339,11 @@ class PaymentEcComponent {
10704
10339
  return true;
10705
10340
  };
10706
10341
  verifyValidate = ($event) => {
10342
+ console.log('[PAYMENT] verifyValidate llamado con:', $event);
10343
+ if (!$event) {
10344
+ this.setLoading(false);
10345
+ return;
10346
+ }
10707
10347
  this.setLoading(true);
10708
10348
  setTimeout(() => this.emitResult(), 1000);
10709
10349
  };
@@ -12108,8 +11748,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
12108
11748
 
12109
11749
  class PriceRangeFilterComponent {
12110
11750
  _filtersService = inject(FiltersService);
12111
- router = inject(Router);
12112
- route = inject(ActivatedRoute);
12113
11751
  priceGap = 1;
12114
11752
  roundStep = 5;
12115
11753
  filter;
@@ -12146,15 +11784,7 @@ class PriceRangeFilterComponent {
12146
11784
  return;
12147
11785
  const min = filter.currentMinPrice ?? filter.minPrice;
12148
11786
  const max = filter.currentMaxPrice ?? filter.maxPrice;
12149
- this.router.navigate([], {
12150
- relativeTo: this.route,
12151
- queryParams: {
12152
- price_min: min !== filter.minPrice ? min : null,
12153
- price_max: max !== filter.maxPrice ? max : null,
12154
- page: null,
12155
- },
12156
- queryParamsHandling: 'merge'
12157
- });
11787
+ this._filtersService.updatePriceRangeFilter(min, max);
12158
11788
  }
12159
11789
  getMinValue(filter) {
12160
11790
  return (filter instanceof PriceRangeFilter && filter.currentMinPrice != null)