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.
- package/README.md +4 -16
- package/esm2022/lib/classes/filters/attributes-filter.mjs +4 -74
- package/esm2022/lib/classes/filters/category-filter.mjs +26 -105
- package/esm2022/lib/classes/filters/filter-factory.mjs +3 -7
- package/esm2022/lib/classes/filters/price_range-filter.mjs +3 -3
- package/esm2022/lib/constants/core.constants.service.mjs +1 -12
- package/esm2022/lib/ec-components/checkout-ec/payment-ec/payment-ec.component.mjs +6 -1
- package/esm2022/lib/ec-components/checkout-ec/payment-ec/payment-methods/mp-redirect-ec/mp-redirect-ec.component.mjs +53 -44
- package/esm2022/lib/ec-components/collection-ec/collection-ec.component.mjs +17 -41
- package/esm2022/lib/ec-components/filters-ec/filters-ec.component.mjs +9 -42
- package/esm2022/lib/ec-components/header-ec/header-ec.component.mjs +5 -13
- package/esm2022/lib/ec-components/price-range-filter/price-range-filter.component.mjs +2 -13
- package/esm2022/lib/ec-components/widgets-ec/redsys-catch-ec/redsys-catch-ec.component.mjs +17 -7
- package/esm2022/lib/ec-services/filters.service.mjs +18 -124
- package/esm2022/lib/ec-services/pagination.service.mjs +22 -70
- package/esm2022/lib/ec-services/products.service.mjs +3 -5
- package/fesm2022/ng-easycommerce-v18.mjs +170 -540
- package/fesm2022/ng-easycommerce-v18.mjs.map +1 -1
- package/lib/classes/filters/attributes-filter.d.ts +0 -24
- package/lib/classes/filters/category-filter.d.ts +3 -30
- package/lib/constants/core.constants.service.d.ts +0 -7
- package/lib/ec-components/checkout-ec/payment-ec/payment-methods/mp-redirect-ec/mp-redirect-ec.component.d.ts +1 -0
- package/lib/ec-components/collection-ec/collection-ec.component.d.ts +4 -5
- package/lib/ec-components/filters-ec/filters-ec.component.d.ts +0 -13
- package/lib/ec-components/price-range-filter/price-range-filter.component.d.ts +0 -2
- package/lib/ec-services/filters.service.d.ts +1 -18
- package/lib/ec-services/pagination.service.d.ts +5 -21
- package/lib/ec-services/products.service.d.ts +1 -1
- 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,
|
|
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(
|
|
2735
|
+
constructor(initialValues) {
|
|
2755
2736
|
super();
|
|
2737
|
+
this.initialValues = initialValues;
|
|
2756
2738
|
this._optionsService.getCategories().subscribe(res => {
|
|
2757
|
-
|
|
2758
|
-
this.
|
|
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
|
-
|
|
2752
|
+
throw new Error("Method not implemented.");
|
|
2771
2753
|
}
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
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 ??
|
|
3175
|
-
this.currentMaxPrice = options.currentMaxPrice ??
|
|
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
|
|
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
|
|
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
|
-
|
|
3860
|
-
extra_params += extra;
|
|
3861
|
-
}
|
|
3694
|
+
extra.split('=')[1] != '' ? extra_params += extra : null;
|
|
3862
3695
|
});
|
|
3863
|
-
|
|
3864
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
4271
|
-
|
|
4272
|
-
|
|
4273
|
-
|
|
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
|
|
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
|
-
|
|
4282
|
-
|
|
4283
|
-
|
|
4284
|
-
|
|
4285
|
-
|
|
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
|
-
})
|
|
4294
|
-
|
|
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
|
|
4047
|
+
getProductsForFilter(paginationSettings, searchValue) {
|
|
4365
4048
|
searchValue ? this.searchValue.set(searchValue) : this.searchValue.set('');
|
|
4366
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
8150
|
-
|
|
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
|
-
|
|
7835
|
+
// Si window.close() lanza excepción, limpiamos el timeout y hacemos fallback inmediato.
|
|
8154
7836
|
}
|
|
8155
|
-
catch {
|
|
8156
|
-
|
|
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 =
|
|
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
|
-
|
|
8409
|
-
|
|
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
|
-
|
|
8441
|
-
|
|
8442
|
-
|
|
8443
|
-
|
|
8444
|
-
|
|
8445
|
-
|
|
8446
|
-
|
|
8447
|
-
|
|
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
|
-
//
|
|
9144
|
-
|
|
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
|
-
|
|
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
|
-
|
|
10290
|
-
|
|
9916
|
+
this.ngZone.run(() => {
|
|
9917
|
+
const data = event?.data;
|
|
9918
|
+
this.onMpMessage(data);
|
|
9919
|
+
});
|
|
10291
9920
|
};
|
|
10292
9921
|
onStorage = (e) => {
|
|
10293
|
-
|
|
10294
|
-
|
|
10295
|
-
|
|
10296
|
-
|
|
10297
|
-
|
|
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
|
-
|
|
10302
|
-
|
|
10303
|
-
|
|
10304
|
-
|
|
10305
|
-
|
|
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
|
-
|
|
10317
|
-
|
|
10318
|
-
|
|
10319
|
-
|
|
10320
|
-
|
|
10321
|
-
|
|
10322
|
-
|
|
10323
|
-
|
|
10324
|
-
|
|
10325
|
-
|
|
10326
|
-
|
|
10327
|
-
|
|
10328
|
-
|
|
10329
|
-
|
|
10330
|
-
|
|
10331
|
-
|
|
10332
|
-
|
|
10333
|
-
|
|
10334
|
-
|
|
10335
|
-
|
|
10336
|
-
|
|
10337
|
-
|
|
10338
|
-
|
|
10339
|
-
|
|
10340
|
-
|
|
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.
|
|
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)
|