ng-easycommerce-v18 0.3.14-beta.1 → 0.3.15-beta.1

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 (30) hide show
  1. package/README.md +9 -2
  2. package/esm2022/lib/classes/filters/filter.mjs +2 -27
  3. package/esm2022/lib/constants/api.constants.service.mjs +42 -28
  4. package/esm2022/lib/ec-components/auth-ec/login-form-ec/login-form-ec.component.mjs +5 -1
  5. package/esm2022/lib/ec-components/auth-ec/register-form-ec/register-form-ec.component.mjs +5 -1
  6. package/esm2022/lib/ec-components/blocks-ec/block-products-ec/block-products-ec.component.mjs +3 -5
  7. package/esm2022/lib/ec-components/cart-ec/cart-item-ec/cart-item-ec.component.mjs +6 -2
  8. package/esm2022/lib/ec-components/checkout-ec/payment-ec/payment-methods/mp-redirect-ec/mp-redirect-ec.component.mjs +79 -9
  9. package/esm2022/lib/ec-components/header-ec/header-ec.component.mjs +45 -34
  10. package/esm2022/lib/ec-components/product-ec/product-ec.component.mjs +95 -5
  11. package/esm2022/lib/ec-components/related-products-ec/related-products-ec.component.mjs +4 -6
  12. package/esm2022/lib/ec-components/widgets-ec/index.mjs +2 -1
  13. package/esm2022/lib/ec-components/widgets-ec/magnizoom-ec/magnizoom-ec.component.mjs +2 -4
  14. package/esm2022/lib/ec-components/widgets-ec/redsys-catch-ec/redsys-catch-ec.component.mjs +246 -0
  15. package/esm2022/lib/ec-services/analytics/facebook-pixel.service.mjs +2 -4
  16. package/esm2022/lib/ec-services/analytics/google-analytics.service.mjs +2 -4
  17. package/esm2022/lib/ec-services/options.service.mjs +3 -27
  18. package/fesm2022/ng-easycommerce-v18.mjs +534 -160
  19. package/fesm2022/ng-easycommerce-v18.mjs.map +1 -1
  20. package/lib/constants/api.constants.service.d.ts +23 -8
  21. package/lib/ec-components/auth-ec/login-form-ec/login-form-ec.component.d.ts +2 -0
  22. package/lib/ec-components/auth-ec/register-form-ec/register-form-ec.component.d.ts +2 -0
  23. package/lib/ec-components/cart-ec/cart-item-ec/cart-item-ec.component.d.ts +5 -0
  24. package/lib/ec-components/checkout-ec/payment-ec/payment-methods/mp-redirect-ec/mp-redirect-ec.component.d.ts +4 -0
  25. package/lib/ec-components/header-ec/header-ec.component.d.ts +5 -1
  26. package/lib/ec-components/product-ec/product-ec.component.d.ts +10 -1
  27. package/lib/ec-components/widgets-ec/index.d.ts +1 -0
  28. package/lib/ec-components/widgets-ec/redsys-catch-ec/redsys-catch-ec.component.d.ts +35 -0
  29. package/lib/ec-services/options.service.d.ts +0 -4
  30. package/package.json +1 -1
@@ -1,7 +1,7 @@
1
1
  import * as i0 from '@angular/core';
2
- import { InjectionToken, makeEnvironmentProviders, inject, PLATFORM_ID, Injectable, Inject, RendererFactory2, afterNextRender, signal, EnvironmentInjector, runInInjectionContext, Component, ChangeDetectorRef, HostListener, CUSTOM_ELEMENTS_SCHEMA, Input, Pipe, Injector, EventEmitter, Output, forwardRef, afterRender, ViewChild, 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, Renderer2, ChangeDetectionStrategy, Directive } from '@angular/core';
3
3
  import * as i1 from '@angular/common';
4
- import { isPlatformServer, DOCUMENT, isPlatformBrowser, AsyncPipe, CommonModule, TitleCasePipe, JsonPipe, UpperCasePipe, Location } from '@angular/common';
4
+ import { DOCUMENT, isPlatformBrowser, AsyncPipe, CommonModule, TitleCasePipe, JsonPipe, UpperCasePipe, Location } from '@angular/common';
5
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';
@@ -14,7 +14,7 @@ import { ToastrService } from 'ngx-toastr';
14
14
  import moment from 'moment';
15
15
  import { StorageMap } from '@ngx-pwa/local-storage';
16
16
  import * as i1$3 from '@angular/forms';
17
- import { Validators, FormBuilder, NG_VALUE_ACCESSOR, ReactiveFormsModule, FormsModule } from '@angular/forms';
17
+ import { Validators, FormsModule, FormBuilder, NG_VALUE_ACCESSOR, ReactiveFormsModule } from '@angular/forms';
18
18
  import { register } from 'swiper/element/bundle';
19
19
  import { register as register$1 } from 'swiper/element';
20
20
  import * as i1$4 from '@angular/platform-browser';
@@ -44,71 +44,89 @@ const provideEnvironment = (environment) => {
44
44
  };
45
45
 
46
46
  /**
47
- * Servicio que provee de datos relacionados con las peticiones a la API
47
+ * Servicio que provee de datos que estan relacionado con las peticiones a la API
48
+ * @export
49
+ * @class ApiConstantsService
48
50
  */
49
51
  class ApiConstantsService {
50
- ssrApiUrl;
51
52
  _localStorage = inject(LocalStorageService);
52
53
  _translate = inject(TranslateService);
54
+ /**
55
+ * Contiene los datos provisto por el frontend en el archivo environment.ts
56
+ */
53
57
  environment = inject(ENVIRONMENT_TOKEN);
54
- platformId = inject(PLATFORM_ID);
55
- _channel;
56
- LOCALE;
57
- SHOP_API_URL = 'shop-api/';
58
- CMS_URL = 'cms/';
59
- constructor(ssrApiUrl) {
60
- this.ssrApiUrl = ssrApiUrl;
61
- this._channel = this.environment.channel;
62
- this.LOCALE = this.environment.locale;
63
- }
64
58
  /**
65
59
  * Canal actual del frontend
66
60
  */
67
61
  get CHANNEL() {
62
+ // Verificar si estamos en el navegador (no en SSR)
68
63
  if (typeof window !== 'undefined') {
64
+ // Primero intenta leer de window.__env (configurado por Docker)
69
65
  const windowEnv = window.__env;
70
- if (windowEnv?.channel)
66
+ if (windowEnv?.channel) {
71
67
  return windowEnv.channel;
68
+ }
72
69
  }
70
+ // Fallback al environment
73
71
  return this._channel;
74
72
  }
75
73
  set CHANNEL(value) {
76
74
  this._channel = value;
77
75
  }
76
+ _channel;
77
+ /**
78
+ * Locale actual del frontend
79
+ */
80
+ LOCALE;
81
+ /**
82
+ * URL para las peticiones a shop-api
83
+ */
84
+ SHOP_API_URL = 'shop-api/';
85
+ /**
86
+ * URL para las peticiones a cms
87
+ */
88
+ CMS_URL = 'cms/';
89
+ constructor() {
90
+ this._channel = this.environment.channel;
91
+ this.LOCALE = this.environment.locale;
92
+ }
78
93
  /**
79
94
  * URL del backend para realizar las peticiones
80
95
  */
81
96
  get API_URL() {
97
+ // Verificar si estamos en el navegador (no en SSR)
82
98
  if (typeof window !== 'undefined') {
99
+ // Primero intenta leer de window.__env (configurado por Docker)
83
100
  const windowEnv = window.__env;
84
- if (windowEnv?.apiUrl)
101
+ if (windowEnv?.apiUrl) {
85
102
  return windowEnv.apiUrl;
103
+ }
86
104
  }
87
- if (isPlatformServer(this.platformId)) {
88
- return this.ssrApiUrl; // ⚡ URL inyectada desde server.ts
89
- }
105
+ // Fallback al environment (para SSR y como backup)
90
106
  return this.environment.apiUrl ?? '';
91
107
  }
92
108
  /**
93
109
  * Retorna la url base
110
+ * @returns {string}
94
111
  */
95
112
  getUrlBase() {
96
113
  return this.API_URL;
97
114
  }
98
115
  /**
99
116
  * Cambia el canal actual
117
+ * @param code
118
+ * @returns
100
119
  */
101
- setChannel(code) {
102
- this.CHANNEL = code;
103
- }
120
+ setChannel(code) { this.CHANNEL = code; }
104
121
  setLocale(locale) {
105
122
  this._localStorage.setItem(this.LOCALE_KEY, locale);
106
123
  this._translate.use(locale.split('_')[0]);
107
124
  this.LOCALE = locale;
108
125
  }
126
+ //Storage key
109
127
  LOCALE_KEY = 'LOCALE';
110
128
  CHANNEL_KEY = 'CHANNEL';
111
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ApiConstantsService, deps: [{ token: 'API_URL' }], target: i0.ɵɵFactoryTarget.Injectable });
129
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ApiConstantsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
112
130
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ApiConstantsService, providedIn: 'root' });
113
131
  }
114
132
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ApiConstantsService, decorators: [{
@@ -116,10 +134,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
116
134
  args: [{
117
135
  providedIn: 'root'
118
136
  }]
119
- }], ctorParameters: () => [{ type: undefined, decorators: [{
120
- type: Inject,
121
- args: ['API_URL']
122
- }] }] });
137
+ }], ctorParameters: () => [] });
123
138
 
124
139
  /**
125
140
  * Servicio que funciona como abstracción para manejar la conexión con la API
@@ -518,10 +533,6 @@ class OptionsService {
518
533
  * Maneja las peticiones a la API
519
534
  */
520
535
  connection = inject(ConnectionService);
521
- /**
522
- * Platform ID para verificar si estamos en el navegador
523
- */
524
- platformId = inject(PLATFORM_ID);
525
536
  /**
526
537
  * Constantes del core
527
538
  */
@@ -632,26 +643,7 @@ class OptionsService {
632
643
  * @returns
633
644
  */
634
645
  removeAccents(str) {
635
- if (isPlatformBrowser(this.platformId) && typeof String.prototype.normalize === 'function') {
636
- return str.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
637
- }
638
- // Fallback para SSR - remover acentos manualmente
639
- return str.replace(/[àáâãäå]/g, 'a')
640
- .replace(/[èéêë]/g, 'e')
641
- .replace(/[ìíîï]/g, 'i')
642
- .replace(/[òóôõö]/g, 'o')
643
- .replace(/[ùúûü]/g, 'u')
644
- .replace(/[ýÿ]/g, 'y')
645
- .replace(/[ñ]/g, 'n')
646
- .replace(/[ç]/g, 'c')
647
- .replace(/[ÀÁÂÃÄÅ]/g, 'A')
648
- .replace(/[ÈÉÊË]/g, 'E')
649
- .replace(/[ÌÍÎÏ]/g, 'I')
650
- .replace(/[ÒÓÔÕÖ]/g, 'O')
651
- .replace(/[ÙÚÛÜ]/g, 'U')
652
- .replace(/[ÝŸ]/g, 'Y')
653
- .replace(/[Ñ]/g, 'N')
654
- .replace(/[Ç]/g, 'C');
646
+ return str.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
655
647
  }
656
648
  /**
657
649
  * Realiza un mapeo de los datos para darle un formato mas entendible a la
@@ -1034,9 +1026,7 @@ class FacebookPixelService {
1034
1026
  this.renderer.appendChild(this.document?.body, new_analityc_script);
1035
1027
  this.enabled = true;
1036
1028
  }
1037
- if (isPlatformBrowser(this.platformId)) {
1038
- setTimeout(() => this.callEvent('initialize'), 1000);
1039
- }
1029
+ setTimeout(() => this.callEvent('initialize'), 1000);
1040
1030
  }
1041
1031
  /**
1042
1032
  * Ejecuta el evento pasado por parametro.
@@ -1253,9 +1243,7 @@ class GoogleAnalyticsService {
1253
1243
  this.renderer.appendChild(this.document?.head, declaration);
1254
1244
  this.enabled = true;
1255
1245
  }
1256
- if (isPlatformBrowser(this.platformId)) {
1257
- setTimeout(() => this.startListeningPageViews(gtm_id), 1000);
1258
- }
1246
+ setTimeout(() => this.startListeningPageViews(gtm_id), 1000);
1259
1247
  }
1260
1248
  /**
1261
1249
  *
@@ -2656,31 +2644,6 @@ class User {
2656
2644
  }
2657
2645
  }
2658
2646
 
2659
- /**
2660
- * Función auxiliar para remover acentos de forma segura para SSR
2661
- */
2662
- function safeRemoveAccents(str) {
2663
- if (typeof window !== 'undefined' && typeof String.prototype.normalize === 'function') {
2664
- return str.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
2665
- }
2666
- // Fallback para SSR - remover acentos manualmente
2667
- return str.replace(/[àáâãäå]/g, 'a')
2668
- .replace(/[èéêë]/g, 'e')
2669
- .replace(/[ìíîï]/g, 'i')
2670
- .replace(/[òóôõö]/g, 'o')
2671
- .replace(/[ùúûü]/g, 'u')
2672
- .replace(/[ýÿ]/g, 'y')
2673
- .replace(/[ñ]/g, 'n')
2674
- .replace(/[ç]/g, 'c')
2675
- .replace(/[ÀÁÂÃÄÅ]/g, 'A')
2676
- .replace(/[ÈÉÊË]/g, 'E')
2677
- .replace(/[ÌÍÎÏ]/g, 'I')
2678
- .replace(/[ÒÓÔÕÖ]/g, 'O')
2679
- .replace(/[ÙÚÛÜ]/g, 'U')
2680
- .replace(/[ÝŸ]/g, 'Y')
2681
- .replace(/[Ñ]/g, 'N')
2682
- .replace(/[Ç]/g, 'C');
2683
- }
2684
2647
  class Filter {
2685
2648
  data = [];
2686
2649
  multi = false;
@@ -2700,7 +2663,7 @@ class Filter {
2700
2663
  throw new Error("Method not implemented.");
2701
2664
  }
2702
2665
  removeAccents = (str) => {
2703
- return safeRemoveAccents(str);
2666
+ return str.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
2704
2667
  };
2705
2668
  setSelected(element, value) {
2706
2669
  //console.log(element, value);
@@ -6191,6 +6154,11 @@ class HeaderEcComponent extends MenuEcComponent {
6191
6154
  hidePrices = false;
6192
6155
  __authService = inject(AuthService);
6193
6156
  _channelService = inject(ChannelService);
6157
+ changeDetector = inject(ChangeDetectorRef);
6158
+ appRouter = inject(Router);
6159
+ platformId = inject(PLATFORM_ID);
6160
+ // Observable del estado de autenticación
6161
+ logged$;
6194
6162
  isAuthenticated$ = this.__authService.isAuthenticated();
6195
6163
  constructor() {
6196
6164
  super();
@@ -6204,11 +6172,26 @@ class HeaderEcComponent extends MenuEcComponent {
6204
6172
  coreConstantsService = inject(CoreConstantsService);
6205
6173
  router = inject(Router);
6206
6174
  cdr = inject(ChangeDetectorRef); // Inyectamos ChangeDetectorRef para forzar la actualización
6207
- platformId = inject(PLATFORM_ID);
6208
6175
  ngOnInit() {
6209
6176
  this.channel = this.coreConstantsService.getChannel();
6210
6177
  this.onWindowScroll();
6211
6178
  this.detectRouteChange(); // Llamamos a la función que detecta el cambio de ruta
6179
+ // Usar el Observable del AuthService
6180
+ this.logged$ = this.__authService.loggedIn$;
6181
+ // Suscribirse al Observable y forzar detección de cambios cuando sea necesario
6182
+ this.logged$.subscribe(isLoggedIn => {
6183
+ this.changeDetector.detectChanges();
6184
+ });
6185
+ if (isPlatformBrowser(this.platformId)) {
6186
+ this.appRouter.events.subscribe(evt => {
6187
+ if (evt instanceof NavigationEnd) {
6188
+ // Forzar detección de cambios después de navegación
6189
+ setTimeout(() => {
6190
+ this.changeDetector.detectChanges();
6191
+ }, 100);
6192
+ }
6193
+ });
6194
+ }
6212
6195
  }
6213
6196
  ngAfterViewInit() {
6214
6197
  this.setupMobileMenu(); // Inicializamos el menú móvil
@@ -6227,21 +6210,17 @@ class HeaderEcComponent extends MenuEcComponent {
6227
6210
  });
6228
6211
  }
6229
6212
  onWindowScroll() {
6230
- if (isPlatformBrowser(this.platformId)) {
6231
- const scrollTop = window.scrollY;
6232
- this.isScrolled = scrollTop > 80;
6233
- }
6213
+ const scrollTop = window.scrollY;
6214
+ this.isScrolled = scrollTop > 80;
6234
6215
  }
6235
6216
  isHomeFunction() {
6236
- if (isPlatformBrowser(this.platformId)) {
6237
- const headerElement = document.querySelector('header');
6238
- if (headerElement) {
6239
- if (this.router.url !== '/home') {
6240
- headerElement.classList.add('show-menu');
6241
- }
6242
- else {
6243
- headerElement.classList.remove('show-menu');
6244
- }
6217
+ const headerElement = document.querySelector('header');
6218
+ if (headerElement) {
6219
+ if (this.router.url !== '/home') {
6220
+ headerElement.classList.add('show-menu');
6221
+ }
6222
+ else {
6223
+ headerElement.classList.remove('show-menu');
6245
6224
  }
6246
6225
  }
6247
6226
  }
@@ -6265,30 +6244,26 @@ class HeaderEcComponent extends MenuEcComponent {
6265
6244
  }
6266
6245
  };
6267
6246
  borrarInput(inputId) {
6268
- if (isPlatformBrowser(this.platformId)) {
6269
- if (inputId) {
6270
- const input = document.getElementById(inputId);
6247
+ if (inputId) {
6248
+ const input = document.getElementById(inputId);
6249
+ if (input) {
6250
+ input.value = '';
6251
+ }
6252
+ }
6253
+ else {
6254
+ const inputs = ['searchInput1'];
6255
+ inputs.forEach((id) => {
6256
+ const input = document.getElementById(id);
6271
6257
  if (input) {
6272
6258
  input.value = '';
6273
6259
  }
6274
- }
6275
- else {
6276
- const inputs = ['searchInput1'];
6277
- inputs.forEach((id) => {
6278
- const input = document.getElementById(id);
6279
- if (input) {
6280
- input.value = '';
6281
- }
6282
- });
6283
- }
6260
+ });
6284
6261
  }
6285
6262
  this.searchValue = '';
6286
6263
  this.coreConstantsService.searchValue = '';
6287
6264
  this.getCollectionSearch();
6288
6265
  }
6289
6266
  setupMobileMenu() {
6290
- if (!isPlatformBrowser(this.platformId))
6291
- return;
6292
6267
  // console.log('setupMobileMenu called');
6293
6268
  const menuMobile = document.querySelector('.menuMobile');
6294
6269
  if (!(menuMobile instanceof HTMLElement))
@@ -6324,8 +6299,6 @@ class HeaderEcComponent extends MenuEcComponent {
6324
6299
  });
6325
6300
  }
6326
6301
  setupSearchInputs() {
6327
- if (!isPlatformBrowser(this.platformId))
6328
- return;
6329
6302
  const inputs = ['searchInput1', 'searchInput2'];
6330
6303
  inputs.forEach(id => {
6331
6304
  const input = document.getElementById(id);
@@ -6922,6 +6895,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
6922
6895
  class ProductEcComponent {
6923
6896
  injector = inject(Injector);
6924
6897
  routerService = inject(Router);
6898
+ _cartService = inject(CartService);
6899
+ _toastService = inject(ToastService);
6900
+ isAddingToCart = signal(false);
6901
+ quantity = signal(1);
6925
6902
  /**
6926
6903
  * Navega al detalle del producto y sube al inicio de la página
6927
6904
  * @param productId ID del producto
@@ -6994,12 +6971,97 @@ class ProductEcComponent {
6994
6971
  const discount = ((originalPrice - salePrice) / originalPrice) * 100;
6995
6972
  return Math.round(discount);
6996
6973
  }
6974
+ plus(stock, multipleQuantity) {
6975
+ console.log('Aumentando cantidad');
6976
+ const current = Number(this.quantity()); // <-- fuerza a número
6977
+ if (multipleQuantity && multipleQuantity > 0) {
6978
+ stock
6979
+ ? (current < stock
6980
+ ? this.quantity.set(current + multipleQuantity)
6981
+ : this._toastService.show('out-of-stock-actually'))
6982
+ : this.quantity.set(current + multipleQuantity);
6983
+ }
6984
+ else {
6985
+ stock
6986
+ ? (current < stock
6987
+ ? this.quantity.set(current + 1)
6988
+ : this._toastService.show('out-of-stock-actually'))
6989
+ : this.quantity.set(current + 1);
6990
+ }
6991
+ }
6992
+ less(multipleQuantity) {
6993
+ console.log('Disminuyendo cantidad');
6994
+ const current = Number(this.quantity()); // <-- fuerza a número
6995
+ if (multipleQuantity && multipleQuantity > 0) {
6996
+ current > multipleQuantity
6997
+ ? this.quantity.set(current - multipleQuantity)
6998
+ : null;
6999
+ }
7000
+ else {
7001
+ current > 1 ? this.quantity.set(current - 1) : null;
7002
+ }
7003
+ }
7004
+ addToCart() {
7005
+ console.log('Añadiendo al carrito');
7006
+ if (this.isAddingToCart() || this.quantity() <= 0)
7007
+ return;
7008
+ // Verificar que tenemos el producto y sus variantes
7009
+ if (!this.product || !this.product.variants || this.product.variants.length === 0) {
7010
+ this._toastService.show('cant-buy');
7011
+ return;
7012
+ }
7013
+ // Verificar stock
7014
+ const firstVariant = this.product.variants[0];
7015
+ if (!firstVariant.stock || firstVariant.stock <= 0) {
7016
+ this._toastService.show('out-of-stock');
7017
+ return;
7018
+ }
7019
+ // Verificar que no se supere el stock disponible
7020
+ if (this.quantity() > firstVariant.stock) {
7021
+ this._toastService.show('out-of-stock-actually');
7022
+ return;
7023
+ }
7024
+ this.isAddingToCart.set(true);
7025
+ try {
7026
+ // Agregar al carrito usando CartService directamente
7027
+ this._cartService.addToCart(this.product, this.quantity(), firstVariant.code);
7028
+ console.log('Producto agregado al carrito exitosamente');
7029
+ // Resetear cantidad a 1 después de agregar al carrito
7030
+ this.quantity.set(1);
7031
+ }
7032
+ catch (error) {
7033
+ console.error('Error al agregar al carrito:', error);
7034
+ this._toastService.show('cant-buy');
7035
+ }
7036
+ setTimeout(() => {
7037
+ this.isAddingToCart.set(false);
7038
+ }, 2000);
7039
+ }
7040
+ checkStock(stock) {
7041
+ if (this.quantity() >= stock)
7042
+ this.quantity.set(stock);
7043
+ }
7044
+ onQuantityChange(event) {
7045
+ const target = event.target;
7046
+ const value = +target.value;
7047
+ // Validar que el valor sea mayor a 0
7048
+ if (value > 0) {
7049
+ this.quantity.set(value);
7050
+ // Validar contra el stock disponible
7051
+ const maxStock = this.product?.variants?.[0]?.stock || this.product?.stock || 0;
7052
+ this.checkStock(maxStock);
7053
+ }
7054
+ else {
7055
+ // Si el valor es 0 o negativo, resetear a 1
7056
+ this.quantity.set(1);
7057
+ }
7058
+ }
6997
7059
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ProductEcComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
6998
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.13", type: ProductEcComponent, isStandalone: true, selector: "app-product-ec", inputs: { product: "product", isProductBox: "isProductBox", isCollection: "isCollection" }, outputs: { loaded: "loaded" }, ngImport: i0, template: "<a [routerLink]=\"['/product', product.id]\" class=\"text-decoration-none producto\">\r\n <!-- Marca especial y descuento -->\r\n <!-- <div *ngIf=\"product.saleprice || (product.special_mark && product.special_mark !== null && product.special_mark !== undefined && product.special_mark.length >0)\"\r\n class=\"marcas\">\r\n <div *ecProductStock=\"product\" [ecProductMini]=\"product.special_mark\"></div>\r\n <ng-container *ngIf=\"shouldShowPrice\">\r\n <div *ecProductStock=\"product\" [ngClass]=\"{'tag-dsc float-right': product.saleprice}\"\r\n [ecProductOff]=\"product\">\r\n </div>\r\n </ng-container>\r\n </div> -->\r\n\r\n <!-- Imagen del producto -->\r\n <div class=\"foto\">\r\n @if(product.picturesdefault){\r\n @if (product.picturesdefault && product.picturesdefault.length > 1 ) {\r\n <img [src]=\"mediaUrl + product.picturesdefault[0]\" alt=\"Imagen principal\" class=\"w-100 pic01\" />\r\n <img [src]=\"mediaUrl + product.picturesdefault[1]\" alt=\"Imagen secundaria\" class=\"w-100 pic02\" />\r\n } @else {\r\n <img [src]=\"mediaUrl + product.picturesdefault[0]\" alt=\"Imagen principal\" class=\"w-100 pic01\" />\r\n }\r\n }\r\n </div>\r\n <!-- Precio -->\r\n\r\n\r\n <!-- Nombre del producto -->\r\n <h6 class=\"title\">{{ product.name | titlecase }}</h6>\r\n\r\n <div class=\"sku\" [innerHTML]=\"product.shortdetails\"></div>\r\n\r\n @if (shouldShowPrice) {\r\n <app-price-ec [price]=\"product.price\" [saleprice]=\"product.saleprice\" class=\"\" />\r\n }\r\n @if(!hidePrices){\r\n @if(!showPricesOnlyToLoggedUsers || isAuthenticated$){\r\n\r\n <div class=\"fixBottom\">\r\n\r\n <!-- Bot\u00F3n de acciones -->\r\n <!-- <ng-container *ecProductStock=\"product; else noStock\"> -->\r\n <!-- Cuando no tiene marca especial o es de tipo 'standard' -->\r\n @if (!product.special_mark || product.special_mark.length === 0 || product.special_mark[0]?.type ===\r\n 'standard') {\r\n <button class=\"btn standard\" [ngClass]=\"isCollection ? 'px-2 w-100 d-sm-block' : 'py-2 px-4'\">\r\n <ng-container *ngIf=\"isCollection; else normalText\">\r\n <span> {{(\"buy\" | translate) | uppercase}} </span>\r\n </ng-container>\r\n <ng-template #normalText>\r\n {{(\"buy\" | translate) | uppercase}}\r\n </ng-template>\r\n </button>\r\n }@else {\r\n <!-- Caso 1: Agotado o Disponible muy pronto -->\r\n @if (product.special_mark[0]?.type === 'out_of_stock' || product.special_mark[0]?.type === 'coming_soon') {\r\n <button class=\"btn\" [ngClass]=\"isCollection ? 'px-2 w-100 d-sm-block' : 'py-2 px-4'\">\r\n @if(isCollection){\r\n <span>{{ product.special_mark[0].name | uppercase }} </span>\r\n }@else {\r\n {{ product.special_mark[0]?.name | uppercase }}\r\n }\r\n </button>}\r\n <!-- Caso 2: Contacto por WhatsApp -->\r\n @if (product.special_mark[0].type === 'whatsapp_contact') {\r\n <button class=\"btn\" [ngClass]=\"isCollection ? 'px-2 w-100 d-sm-block' : 'py-2 px-4'\"\r\n (click)=\"openWhatsApp(product.special_mark[0]?.whatsappContact)\">\r\n @if(isCollection){\r\n <span>{{ product.special_mark[0]?.name | uppercase }}</span>\r\n }@else {\r\n {{ product.special_mark[0]?.name | uppercase }}\r\n }\r\n\r\n </button>\r\n }\r\n <!-- Caso 3: Solicitar m\u00E1s informaci\u00F3n -->\r\n @if (product.special_mark[0]?.type === 'more_info') {\r\n <button class=\"btn\" [ngClass]=\"isCollection ? 'px-2 w-100 d-sm-block' : 'py-2 px-4'\">\r\n @if(isCollection){\r\n <span>{{ product.special_mark[0]?.name | uppercase }}</span>\r\n }@else {\r\n {{ product.special_mark[0]?.name | uppercase }}\r\n }\r\n </button>\r\n }\r\n }\r\n </div>\r\n }}\r\n</a>", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i1.UpperCasePipe, name: "uppercase" }, { kind: "pipe", type: i1.TitleCasePipe, name: "titlecase" }, { kind: "component", type: PriceEcComponent, selector: "app-price-ec", inputs: ["price", "saleprice", "basePrice", "taxeAmount", "taxes", "priceSize", "showTaxLegendOnly", "disableTaxInfo", "customPriceTemplate", "customSalePriceTemplate", "customSimplePriceTemplate", "customSimpleSalePriceTemplate", "customTaxTemplate", "customOnlyTaxLabelTemplate"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }] });
7060
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.13", type: ProductEcComponent, isStandalone: true, selector: "app-product-ec", inputs: { product: "product", isProductBox: "isProductBox", isCollection: "isCollection" }, outputs: { loaded: "loaded" }, ngImport: i0, template: "<a [routerLink]=\"['/product', product.id]\" class=\"text-decoration-none producto\">\r\n <!-- Marca especial y descuento -->\r\n <!-- <div *ngIf=\"product.saleprice || (product.special_mark && product.special_mark !== null && product.special_mark !== undefined && product.special_mark.length >0)\"\r\n class=\"marcas\">\r\n <div *ecProductStock=\"product\" [ecProductMini]=\"product.special_mark\"></div>\r\n <ng-container *ngIf=\"shouldShowPrice\">\r\n <div *ecProductStock=\"product\" [ngClass]=\"{'tag-dsc float-right': product.saleprice}\"\r\n [ecProductOff]=\"product\">\r\n </div>\r\n </ng-container>\r\n </div> -->\r\n\r\n <!-- Imagen del producto -->\r\n <div class=\"foto\">\r\n @if(product.picturesdefault){\r\n @if (product.picturesdefault && product.picturesdefault.length > 1 ) {\r\n <img [src]=\"mediaUrl + product.picturesdefault[0]\" alt=\"Imagen principal\" class=\"w-100 pic01\" />\r\n <img [src]=\"mediaUrl + product.picturesdefault[1]\" alt=\"Imagen secundaria\" class=\"w-100 pic02\" />\r\n } @else {\r\n <img [src]=\"mediaUrl + product.picturesdefault[0]\" alt=\"Imagen principal\" class=\"w-100 pic01\" />\r\n }\r\n }\r\n </div>\r\n <!-- Precio -->\r\n\r\n\r\n <!-- Nombre del producto -->\r\n <h6 class=\"title\">{{ product.name | titlecase }}</h6>\r\n\r\n <div class=\"sku\" [innerHTML]=\"product.shortdetails\"></div>\r\n\r\n @if (shouldShowPrice) {\r\n <app-price-ec [price]=\"product.price\" [saleprice]=\"product.saleprice\" class=\"\" />\r\n }\r\n @if(!hidePrices){\r\n @if(!showPricesOnlyToLoggedUsers || isAuthenticated$){\r\n\r\n <div class=\"fixBottom\">\r\n\r\n <!-- Bot\u00F3n de acciones -->\r\n <!-- <ng-container *ecProductStock=\"product; else noStock\"> -->\r\n <!-- Cuando no tiene marca especial o es de tipo 'standard' -->\r\n @if (!product.special_mark || product.special_mark.length === 0 || product.special_mark[0]?.type ===\r\n 'standard') {\r\n <button class=\"btn standard\" [ngClass]=\"isCollection ? 'px-2 w-100 d-sm-block' : 'py-2 px-4'\">\r\n <ng-container *ngIf=\"isCollection; else normalText\">\r\n <span> {{(\"buy\" | translate) | uppercase}} </span>\r\n </ng-container>\r\n <ng-template #normalText>\r\n {{(\"buy\" | translate) | uppercase}}\r\n </ng-template>\r\n </button>\r\n }@else {\r\n <!-- Caso 1: Agotado o Disponible muy pronto -->\r\n @if (product.special_mark[0]?.type === 'out_of_stock' || product.special_mark[0]?.type === 'coming_soon') {\r\n <button class=\"btn\" [ngClass]=\"isCollection ? 'px-2 w-100 d-sm-block' : 'py-2 px-4'\">\r\n @if(isCollection){\r\n <span>{{ product.special_mark[0].name | uppercase }} </span>\r\n }@else {\r\n {{ product.special_mark[0]?.name | uppercase }}\r\n }\r\n </button>}\r\n <!-- Caso 2: Contacto por WhatsApp -->\r\n @if (product.special_mark[0].type === 'whatsapp_contact') {\r\n <button class=\"btn\" [ngClass]=\"isCollection ? 'px-2 w-100 d-sm-block' : 'py-2 px-4'\"\r\n (click)=\"openWhatsApp(product.special_mark[0]?.whatsappContact)\">\r\n @if(isCollection){\r\n <span>{{ product.special_mark[0]?.name | uppercase }}</span>\r\n }@else {\r\n {{ product.special_mark[0]?.name | uppercase }}\r\n }\r\n\r\n </button>\r\n }\r\n <!-- Caso 3: Solicitar m\u00E1s informaci\u00F3n -->\r\n @if (product.special_mark[0]?.type === 'more_info') {\r\n <button class=\"btn\" [ngClass]=\"isCollection ? 'px-2 w-100 d-sm-block' : 'py-2 px-4'\">\r\n @if(isCollection){\r\n <span>{{ product.special_mark[0]?.name | uppercase }}</span>\r\n }@else {\r\n {{ product.special_mark[0]?.name | uppercase }}\r\n }\r\n </button>\r\n }\r\n }\r\n </div>\r\n }}\r\n</a>", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i1.UpperCasePipe, name: "uppercase" }, { kind: "pipe", type: i1.TitleCasePipe, name: "titlecase" }, { kind: "component", type: PriceEcComponent, selector: "app-price-ec", inputs: ["price", "saleprice", "basePrice", "taxeAmount", "taxes", "priceSize", "showTaxLegendOnly", "disableTaxInfo", "customPriceTemplate", "customSalePriceTemplate", "customSimplePriceTemplate", "customSimpleSalePriceTemplate", "customTaxTemplate", "customOnlyTaxLabelTemplate"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }, { kind: "ngmodule", type: FormsModule }] });
6999
7061
  }
7000
7062
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ProductEcComponent, decorators: [{
7001
7063
  type: Component,
7002
- args: [{ selector: 'app-product-ec', standalone: true, imports: [CommonModule, PriceEcComponent, RouterLink, TranslateModule], template: "<a [routerLink]=\"['/product', product.id]\" class=\"text-decoration-none producto\">\r\n <!-- Marca especial y descuento -->\r\n <!-- <div *ngIf=\"product.saleprice || (product.special_mark && product.special_mark !== null && product.special_mark !== undefined && product.special_mark.length >0)\"\r\n class=\"marcas\">\r\n <div *ecProductStock=\"product\" [ecProductMini]=\"product.special_mark\"></div>\r\n <ng-container *ngIf=\"shouldShowPrice\">\r\n <div *ecProductStock=\"product\" [ngClass]=\"{'tag-dsc float-right': product.saleprice}\"\r\n [ecProductOff]=\"product\">\r\n </div>\r\n </ng-container>\r\n </div> -->\r\n\r\n <!-- Imagen del producto -->\r\n <div class=\"foto\">\r\n @if(product.picturesdefault){\r\n @if (product.picturesdefault && product.picturesdefault.length > 1 ) {\r\n <img [src]=\"mediaUrl + product.picturesdefault[0]\" alt=\"Imagen principal\" class=\"w-100 pic01\" />\r\n <img [src]=\"mediaUrl + product.picturesdefault[1]\" alt=\"Imagen secundaria\" class=\"w-100 pic02\" />\r\n } @else {\r\n <img [src]=\"mediaUrl + product.picturesdefault[0]\" alt=\"Imagen principal\" class=\"w-100 pic01\" />\r\n }\r\n }\r\n </div>\r\n <!-- Precio -->\r\n\r\n\r\n <!-- Nombre del producto -->\r\n <h6 class=\"title\">{{ product.name | titlecase }}</h6>\r\n\r\n <div class=\"sku\" [innerHTML]=\"product.shortdetails\"></div>\r\n\r\n @if (shouldShowPrice) {\r\n <app-price-ec [price]=\"product.price\" [saleprice]=\"product.saleprice\" class=\"\" />\r\n }\r\n @if(!hidePrices){\r\n @if(!showPricesOnlyToLoggedUsers || isAuthenticated$){\r\n\r\n <div class=\"fixBottom\">\r\n\r\n <!-- Bot\u00F3n de acciones -->\r\n <!-- <ng-container *ecProductStock=\"product; else noStock\"> -->\r\n <!-- Cuando no tiene marca especial o es de tipo 'standard' -->\r\n @if (!product.special_mark || product.special_mark.length === 0 || product.special_mark[0]?.type ===\r\n 'standard') {\r\n <button class=\"btn standard\" [ngClass]=\"isCollection ? 'px-2 w-100 d-sm-block' : 'py-2 px-4'\">\r\n <ng-container *ngIf=\"isCollection; else normalText\">\r\n <span> {{(\"buy\" | translate) | uppercase}} </span>\r\n </ng-container>\r\n <ng-template #normalText>\r\n {{(\"buy\" | translate) | uppercase}}\r\n </ng-template>\r\n </button>\r\n }@else {\r\n <!-- Caso 1: Agotado o Disponible muy pronto -->\r\n @if (product.special_mark[0]?.type === 'out_of_stock' || product.special_mark[0]?.type === 'coming_soon') {\r\n <button class=\"btn\" [ngClass]=\"isCollection ? 'px-2 w-100 d-sm-block' : 'py-2 px-4'\">\r\n @if(isCollection){\r\n <span>{{ product.special_mark[0].name | uppercase }} </span>\r\n }@else {\r\n {{ product.special_mark[0]?.name | uppercase }}\r\n }\r\n </button>}\r\n <!-- Caso 2: Contacto por WhatsApp -->\r\n @if (product.special_mark[0].type === 'whatsapp_contact') {\r\n <button class=\"btn\" [ngClass]=\"isCollection ? 'px-2 w-100 d-sm-block' : 'py-2 px-4'\"\r\n (click)=\"openWhatsApp(product.special_mark[0]?.whatsappContact)\">\r\n @if(isCollection){\r\n <span>{{ product.special_mark[0]?.name | uppercase }}</span>\r\n }@else {\r\n {{ product.special_mark[0]?.name | uppercase }}\r\n }\r\n\r\n </button>\r\n }\r\n <!-- Caso 3: Solicitar m\u00E1s informaci\u00F3n -->\r\n @if (product.special_mark[0]?.type === 'more_info') {\r\n <button class=\"btn\" [ngClass]=\"isCollection ? 'px-2 w-100 d-sm-block' : 'py-2 px-4'\">\r\n @if(isCollection){\r\n <span>{{ product.special_mark[0]?.name | uppercase }}</span>\r\n }@else {\r\n {{ product.special_mark[0]?.name | uppercase }}\r\n }\r\n </button>\r\n }\r\n }\r\n </div>\r\n }}\r\n</a>" }]
7064
+ args: [{ selector: 'app-product-ec', standalone: true, imports: [CommonModule, PriceEcComponent, RouterLink, TranslateModule, FormsModule], template: "<a [routerLink]=\"['/product', product.id]\" class=\"text-decoration-none producto\">\r\n <!-- Marca especial y descuento -->\r\n <!-- <div *ngIf=\"product.saleprice || (product.special_mark && product.special_mark !== null && product.special_mark !== undefined && product.special_mark.length >0)\"\r\n class=\"marcas\">\r\n <div *ecProductStock=\"product\" [ecProductMini]=\"product.special_mark\"></div>\r\n <ng-container *ngIf=\"shouldShowPrice\">\r\n <div *ecProductStock=\"product\" [ngClass]=\"{'tag-dsc float-right': product.saleprice}\"\r\n [ecProductOff]=\"product\">\r\n </div>\r\n </ng-container>\r\n </div> -->\r\n\r\n <!-- Imagen del producto -->\r\n <div class=\"foto\">\r\n @if(product.picturesdefault){\r\n @if (product.picturesdefault && product.picturesdefault.length > 1 ) {\r\n <img [src]=\"mediaUrl + product.picturesdefault[0]\" alt=\"Imagen principal\" class=\"w-100 pic01\" />\r\n <img [src]=\"mediaUrl + product.picturesdefault[1]\" alt=\"Imagen secundaria\" class=\"w-100 pic02\" />\r\n } @else {\r\n <img [src]=\"mediaUrl + product.picturesdefault[0]\" alt=\"Imagen principal\" class=\"w-100 pic01\" />\r\n }\r\n }\r\n </div>\r\n <!-- Precio -->\r\n\r\n\r\n <!-- Nombre del producto -->\r\n <h6 class=\"title\">{{ product.name | titlecase }}</h6>\r\n\r\n <div class=\"sku\" [innerHTML]=\"product.shortdetails\"></div>\r\n\r\n @if (shouldShowPrice) {\r\n <app-price-ec [price]=\"product.price\" [saleprice]=\"product.saleprice\" class=\"\" />\r\n }\r\n @if(!hidePrices){\r\n @if(!showPricesOnlyToLoggedUsers || isAuthenticated$){\r\n\r\n <div class=\"fixBottom\">\r\n\r\n <!-- Bot\u00F3n de acciones -->\r\n <!-- <ng-container *ecProductStock=\"product; else noStock\"> -->\r\n <!-- Cuando no tiene marca especial o es de tipo 'standard' -->\r\n @if (!product.special_mark || product.special_mark.length === 0 || product.special_mark[0]?.type ===\r\n 'standard') {\r\n <button class=\"btn standard\" [ngClass]=\"isCollection ? 'px-2 w-100 d-sm-block' : 'py-2 px-4'\">\r\n <ng-container *ngIf=\"isCollection; else normalText\">\r\n <span> {{(\"buy\" | translate) | uppercase}} </span>\r\n </ng-container>\r\n <ng-template #normalText>\r\n {{(\"buy\" | translate) | uppercase}}\r\n </ng-template>\r\n </button>\r\n }@else {\r\n <!-- Caso 1: Agotado o Disponible muy pronto -->\r\n @if (product.special_mark[0]?.type === 'out_of_stock' || product.special_mark[0]?.type === 'coming_soon') {\r\n <button class=\"btn\" [ngClass]=\"isCollection ? 'px-2 w-100 d-sm-block' : 'py-2 px-4'\">\r\n @if(isCollection){\r\n <span>{{ product.special_mark[0].name | uppercase }} </span>\r\n }@else {\r\n {{ product.special_mark[0]?.name | uppercase }}\r\n }\r\n </button>}\r\n <!-- Caso 2: Contacto por WhatsApp -->\r\n @if (product.special_mark[0].type === 'whatsapp_contact') {\r\n <button class=\"btn\" [ngClass]=\"isCollection ? 'px-2 w-100 d-sm-block' : 'py-2 px-4'\"\r\n (click)=\"openWhatsApp(product.special_mark[0]?.whatsappContact)\">\r\n @if(isCollection){\r\n <span>{{ product.special_mark[0]?.name | uppercase }}</span>\r\n }@else {\r\n {{ product.special_mark[0]?.name | uppercase }}\r\n }\r\n\r\n </button>\r\n }\r\n <!-- Caso 3: Solicitar m\u00E1s informaci\u00F3n -->\r\n @if (product.special_mark[0]?.type === 'more_info') {\r\n <button class=\"btn\" [ngClass]=\"isCollection ? 'px-2 w-100 d-sm-block' : 'py-2 px-4'\">\r\n @if(isCollection){\r\n <span>{{ product.special_mark[0]?.name | uppercase }}</span>\r\n }@else {\r\n {{ product.special_mark[0]?.name | uppercase }}\r\n }\r\n </button>\r\n }\r\n }\r\n </div>\r\n }}\r\n</a>" }]
7003
7065
  }], ctorParameters: () => [], propDecorators: { product: [{
7004
7066
  type: Input,
7005
7067
  args: [{
@@ -7072,7 +7134,7 @@ class BlockProductsEcComponent extends BlockEcComponent {
7072
7134
  * Permite personalización de las imágenes de las flechas mediante @Input.
7073
7135
  */
7074
7136
  setupSwiperNavigation() {
7075
- if (this.meta?.styles?.carrousel !== false && isPlatformBrowser(this.platformId)) {
7137
+ if (this.meta?.styles?.carrousel !== false) {
7076
7138
  // Usar setTimeout para asegurar que el swiper esté inicializado
7077
7139
  setTimeout(() => {
7078
7140
  this.initializeSwiperWithCustomNavigation();
@@ -7084,8 +7146,6 @@ class BlockProductsEcComponent extends BlockEcComponent {
7084
7146
  * Esta función puede ser movida al componente base para reutilización.
7085
7147
  */
7086
7148
  initializeSwiperWithCustomNavigation() {
7087
- if (!isPlatformBrowser(this.platformId))
7088
- return;
7089
7149
  const prevButton = document.getElementById(`${this.meta?.code}-prev`);
7090
7150
  const nextButton = document.getElementById(`${this.meta?.code}-next`);
7091
7151
  const swiperElement = document.getElementById(this.meta?.code);
@@ -7445,9 +7505,7 @@ class MagnizoomEcComponent {
7445
7505
  this.image = this.document.createElement('img');
7446
7506
  this.image.onload = () => {
7447
7507
  this.lensSize = { width: this.image.width / 2, height: this.image.height / 2 };
7448
- if (isPlatformBrowser(this.platformId)) {
7449
- setTimeout(() => this.render());
7450
- }
7508
+ setTimeout(() => this.render());
7451
7509
  };
7452
7510
  this.image.src = src;
7453
7511
  }
@@ -7577,6 +7635,265 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
7577
7635
  args: ['mainCanvas', { static: true }]
7578
7636
  }] } });
7579
7637
 
7638
+ class ComponentHelper {
7639
+ constructor() {
7640
+ this.ecOnConstruct();
7641
+ }
7642
+ ecOnInit = (params = {}) => {
7643
+ };
7644
+ ecOnConstruct = (params = {}) => {
7645
+ };
7646
+ hasParams = (params, searched) => {
7647
+ if (!params || !searched)
7648
+ return false;
7649
+ const q = searched.trim().toLowerCase();
7650
+ return params.some(p => {
7651
+ const code = p?.['code']?.toString().toLowerCase() ?? '';
7652
+ // Primero chequeo exacto, y si no, parcial
7653
+ return code === q || code.includes(q);
7654
+ });
7655
+ };
7656
+ navigateOnRouter(router, url) {
7657
+ router.navigateByUrl(`/${url}`);
7658
+ }
7659
+ }
7660
+
7661
+ class RedsysCatchEcComponent extends ComponentHelper {
7662
+ activedRoute;
7663
+ router;
7664
+ checkoutService;
7665
+ renderer;
7666
+ elementRef;
7667
+ document;
7668
+ platformId;
7669
+ message = '';
7670
+ subscription = null;
7671
+ isMobile = false;
7672
+ sessionId = '';
7673
+ constructor(activedRoute, router, checkoutService, renderer, elementRef, document, platformId) {
7674
+ super();
7675
+ this.activedRoute = activedRoute;
7676
+ this.router = router;
7677
+ this.checkoutService = checkoutService;
7678
+ this.renderer = renderer;
7679
+ this.elementRef = elementRef;
7680
+ this.document = document;
7681
+ this.platformId = platformId;
7682
+ if (isPlatformBrowser(this.platformId)) {
7683
+ this.detectMobile();
7684
+ this.sessionId = this.generateSessionId();
7685
+ // Verificar si debe procesar esta pestaña
7686
+ if (this.shouldProcessPayment()) {
7687
+ this.hideHeaderFooter();
7688
+ }
7689
+ else {
7690
+ // Si no debe procesar, cerrar esta pestaña y redirigir a la principal
7691
+ this.closeRedundantTab();
7692
+ return;
7693
+ }
7694
+ }
7695
+ this.ecOnConstruct();
7696
+ }
7697
+ detectMobile() {
7698
+ if (isPlatformBrowser(this.platformId) && typeof window !== 'undefined') {
7699
+ const userAgent = window.navigator.userAgent.toLowerCase();
7700
+ this.isMobile = /android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini|mobile/i.test(userAgent) ||
7701
+ (window.innerWidth <= 768);
7702
+ }
7703
+ }
7704
+ generateSessionId() {
7705
+ return `catch_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
7706
+ }
7707
+ shouldProcessPayment() {
7708
+ if (!isPlatformBrowser(this.platformId)) {
7709
+ return false; // En el servidor no procesar
7710
+ }
7711
+ if (!this.isMobile) {
7712
+ return true; // En desktop siempre procesar
7713
+ }
7714
+ // En mobile, verificar si ya hay otra pestaña procesando
7715
+ const processingTab = localStorage.getItem('mp_processing_tab');
7716
+ const currentTime = Date.now();
7717
+ const processingTabTime = localStorage.getItem('mp_processing_tab_time');
7718
+ // Si no hay pestaña procesando o la anterior es muy antigua (más de 30 segundos)
7719
+ if (!processingTab || !processingTabTime || (currentTime - parseInt(processingTabTime)) > 30000) {
7720
+ // Esta pestaña será la que procese
7721
+ localStorage.setItem('mp_processing_tab', this.sessionId);
7722
+ localStorage.setItem('mp_processing_tab_time', currentTime.toString());
7723
+ return true;
7724
+ }
7725
+ // Si ya hay otra pestaña procesando
7726
+ return processingTab === this.sessionId;
7727
+ }
7728
+ closeRedundantTab() {
7729
+ console.log('Cerrando pestaña redundante...');
7730
+ // Intentar cerrar la pestaña
7731
+ if (isPlatformBrowser(this.platformId) && typeof window !== 'undefined') {
7732
+ // Redirigir a la página principal en lugar de cerrar
7733
+ window.location.href = '/checkout/order_success';
7734
+ }
7735
+ }
7736
+ ngOnInit() {
7737
+ this.subscription = combineLatest([
7738
+ this.activedRoute.params,
7739
+ this.activedRoute.queryParams
7740
+ ]).subscribe(([queryRouter, queryParams]) => {
7741
+ let state;
7742
+ state = queryRouter['state'];
7743
+ if (state && state === 'statuspayment') {
7744
+ state = queryParams['status'];
7745
+ }
7746
+ console.log('PARAMETROS STATE -> ', state);
7747
+ this.handlePaymentState(state, queryParams);
7748
+ });
7749
+ this.ecOnInit();
7750
+ }
7751
+ ngOnDestroy() {
7752
+ if (this.subscription) {
7753
+ this.subscription.unsubscribe();
7754
+ }
7755
+ this.showHeaderFooter();
7756
+ // Limpiar marcadores de mobile si esta pestaña estaba procesando
7757
+ if (this.isMobile && isPlatformBrowser(this.platformId)) {
7758
+ const processingTab = localStorage.getItem('mp_processing_tab');
7759
+ if (processingTab === this.sessionId) {
7760
+ localStorage.removeItem('mp_processing_tab');
7761
+ localStorage.removeItem('mp_processing_tab_time');
7762
+ }
7763
+ }
7764
+ // this.ecOnDestroy(); // Removido si no existe en ComponentHelper
7765
+ }
7766
+ hideHeaderFooter() {
7767
+ // Usar Renderer2 en lugar de jQuery para manipular el DOM
7768
+ const header = this.document.querySelector('header');
7769
+ const footer = this.document.querySelector('footer');
7770
+ if (header) {
7771
+ this.renderer.setStyle(header, 'display', 'none');
7772
+ }
7773
+ if (footer) {
7774
+ this.renderer.setStyle(footer, 'display', 'none');
7775
+ }
7776
+ }
7777
+ showHeaderFooter() {
7778
+ // Restaurar la visibilidad al destruir el componente
7779
+ const header = this.document.querySelector('header');
7780
+ const footer = this.document.querySelector('footer');
7781
+ if (header) {
7782
+ this.renderer.removeStyle(header, 'display');
7783
+ }
7784
+ if (footer) {
7785
+ this.renderer.removeStyle(footer, 'display');
7786
+ }
7787
+ }
7788
+ handlePaymentState(state, queryParams) {
7789
+ // Solo procesar el estado si esta pestaña está autorizada
7790
+ if (this.isMobile && !this.shouldProcessPayment()) {
7791
+ console.log('Pestaña no autorizada para procesar el pago, redirigiendo...');
7792
+ this.closeRedundantTab();
7793
+ return;
7794
+ }
7795
+ switch (state) {
7796
+ case '200':
7797
+ this.setStateInLocal('Su pago fue procesado con éxito.', 'success');
7798
+ this.storeTotalAmount(queryParams);
7799
+ this.redirectToSuccess();
7800
+ break;
7801
+ case 'success':
7802
+ this.setStateInLocal('Su pago fue procesado con éxito.', state);
7803
+ this.storeTotalAmount(queryParams);
7804
+ this.redirectToSuccess();
7805
+ break;
7806
+ case 'pending':
7807
+ this.setStateInLocal('Su pago fue procesado con éxito.', state);
7808
+ this.redirectToSuccess();
7809
+ break;
7810
+ case 'failure':
7811
+ this.setStateInLocal('Se ha cancelado el proceso de pago.', state);
7812
+ this.storeTotalAmount(queryParams);
7813
+ this.redirectToCheckout();
7814
+ break;
7815
+ case 'cancel':
7816
+ this.setStateInLocal('Se ha cancelado el proceso de pago.', state);
7817
+ this.redirectToCheckout();
7818
+ break;
7819
+ case 'ok':
7820
+ this.setStateInSesion('Su pago fue procesado con éxito.', state);
7821
+ break;
7822
+ case 'challenge':
7823
+ this.setStateInSesion('Redirigiendo a autenticación del banco emisor.', state);
7824
+ break;
7825
+ case 'error':
7826
+ this.setStateInSesion('Algo no salio bien en la validación de sus datos.', state);
7827
+ break;
7828
+ case '0':
7829
+ this.setStateInLocal('Se ha cancelado el proceso de pago.', 'failure');
7830
+ this.redirectToCheckout();
7831
+ break;
7832
+ default:
7833
+ break;
7834
+ }
7835
+ }
7836
+ redirectToSuccess() {
7837
+ // Limpiar marcadores de procesamiento
7838
+ if (this.isMobile && isPlatformBrowser(this.platformId)) {
7839
+ localStorage.removeItem('mp_processing_tab');
7840
+ localStorage.removeItem('mp_processing_tab_time');
7841
+ }
7842
+ // Redirigir después de un breve delay
7843
+ setTimeout(() => {
7844
+ this.router.navigate(['/checkout/order_success']);
7845
+ }, 2000);
7846
+ }
7847
+ redirectToCheckout() {
7848
+ // Limpiar marcadores de procesamiento
7849
+ if (this.isMobile && isPlatformBrowser(this.platformId)) {
7850
+ localStorage.removeItem('mp_processing_tab');
7851
+ localStorage.removeItem('mp_processing_tab_time');
7852
+ }
7853
+ // Redirigir después de un breve delay
7854
+ setTimeout(() => {
7855
+ this.router.navigate(['/checkout']);
7856
+ }, 2000);
7857
+ }
7858
+ storeTotalAmount(queryParams) {
7859
+ const totalAmount = queryParams['total_amount'];
7860
+ if (totalAmount && isPlatformBrowser(this.platformId)) {
7861
+ localStorage.setItem('total_amount', totalAmount);
7862
+ }
7863
+ }
7864
+ setStateInLocal = (mensaje, state) => {
7865
+ this.message = mensaje;
7866
+ if (isPlatformBrowser(this.platformId)) {
7867
+ localStorage.setItem('state', state);
7868
+ if (typeof sessionStorage !== 'undefined') {
7869
+ sessionStorage.setItem('modalnews', 'false');
7870
+ }
7871
+ }
7872
+ };
7873
+ setStateInSesion = (mensaje, state) => {
7874
+ this.message = mensaje;
7875
+ if (isPlatformBrowser(this.platformId)) {
7876
+ if (typeof sessionStorage !== 'undefined') {
7877
+ sessionStorage.setItem('state', state);
7878
+ sessionStorage.setItem('modalnews', 'false');
7879
+ }
7880
+ localStorage.setItem('state', state);
7881
+ }
7882
+ };
7883
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: RedsysCatchEcComponent, deps: [{ token: i2.ActivatedRoute }, { token: i2.Router }, { token: CheckoutService }, { token: i0.Renderer2 }, { token: i0.ElementRef }, { token: DOCUMENT }, { token: PLATFORM_ID }], target: i0.ɵɵFactoryTarget.Component });
7884
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: RedsysCatchEcComponent, isStandalone: true, selector: "app-redsys-catch-ec", usesInheritance: true, ngImport: i0, template: "<div id=\"container\">\r\n <div class=\"row\">\r\n <div class=\"col align-self-center\">\r\n <h4 class=\"titpage center-block text-center font-nexa font-lg my-3\">{{ message | uppercase }}</h4>\r\n </div>\r\n </div>\r\n <div class=\"row\">\r\n <div class=\"col align-self-center\">\r\n <h5 class=\"center-block text-center font-nexa my-3\">Redirigiendo en segundos...</h5>\r\n <br>\r\n <div class=\"d-flex flex-column jusitfy-content-center align-items-center\">\r\n <app-loading-full-ec></app-loading-full-ec>\r\n </div>\r\n </div>\r\n </div>\r\n</div>", styles: [".loader{border:16px solid #f3f3f3;border-top:16px solid #dc3545;border-radius:50%;width:50px;height:50px;animation:spin 2s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.flex-container{display:flex;justify-content:center;align-items:center;height:80vh;width:100%}.flex-container>div{width:90%;height:100px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "pipe", type: i1.UpperCasePipe, name: "uppercase" }, { kind: "component", type: LoadingFullEcComponent, selector: "app-loading-full-ec" }] });
7885
+ }
7886
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: RedsysCatchEcComponent, decorators: [{
7887
+ type: Component,
7888
+ args: [{ selector: 'app-redsys-catch-ec', standalone: true, imports: [CommonModule, LoadingFullEcComponent], template: "<div id=\"container\">\r\n <div class=\"row\">\r\n <div class=\"col align-self-center\">\r\n <h4 class=\"titpage center-block text-center font-nexa font-lg my-3\">{{ message | uppercase }}</h4>\r\n </div>\r\n </div>\r\n <div class=\"row\">\r\n <div class=\"col align-self-center\">\r\n <h5 class=\"center-block text-center font-nexa my-3\">Redirigiendo en segundos...</h5>\r\n <br>\r\n <div class=\"d-flex flex-column jusitfy-content-center align-items-center\">\r\n <app-loading-full-ec></app-loading-full-ec>\r\n </div>\r\n </div>\r\n </div>\r\n</div>", styles: [".loader{border:16px solid #f3f3f3;border-top:16px solid #dc3545;border-radius:50%;width:50px;height:50px;animation:spin 2s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.flex-container{display:flex;justify-content:center;align-items:center;height:80vh;width:100%}.flex-container>div{width:90%;height:100px}\n"] }]
7889
+ }], ctorParameters: () => [{ type: i2.ActivatedRoute }, { type: i2.Router }, { type: CheckoutService }, { type: i0.Renderer2 }, { type: i0.ElementRef }, { type: Document, decorators: [{
7890
+ type: Inject,
7891
+ args: [DOCUMENT]
7892
+ }] }, { type: undefined, decorators: [{
7893
+ type: Inject,
7894
+ args: [PLATFORM_ID]
7895
+ }] }] });
7896
+
7580
7897
  // export * from './rating-ec/rating-ec.component';
7581
7898
 
7582
7899
  class BlockFormContactEcComponent extends BlockEcComponent {
@@ -7852,6 +8169,7 @@ class LoginFormEcComponent {
7852
8169
  _formBuilder = inject(FormBuilder);
7853
8170
  _toastService = inject(ToastService);
7854
8171
  _router = inject(Router);
8172
+ showPassword = false;
7855
8173
  /**
7856
8174
  * Parametro para indicar si tras loguear
7857
8175
  * debe redireccionar o no.
@@ -7940,6 +8258,9 @@ class LoginFormEcComponent {
7940
8258
  ? resolverFunction()
7941
8259
  : this._router.navigateByUrl(this.redirectTo);
7942
8260
  }
8261
+ togglePassword() {
8262
+ this.showPassword = !this.showPassword;
8263
+ }
7943
8264
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: LoginFormEcComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
7944
8265
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.13", type: LoginFormEcComponent, isStandalone: true, selector: "app-login-form-ec", inputs: { redirect: "redirect", redirectTo: "redirectTo", inCart: "inCart" }, outputs: { ready: "ready" }, ngImport: i0, template: "<div class=\"d-flex flex-column position-relative\">\r\n <h1 class=\"right-line ff-ubuntu-light mb-4\"><span>Ingresar</span></h1>\r\n <p class=\"ff-ubuntu-light font-sm pr-4 mb-4\">\r\n Si ya est\u00E1s registrado. Ingresa en tu cuenta con tu email y la\r\n contrase\u00F1a adecuada.\r\n </p>\r\n <div class=\"w-md-50 w-100 text-center\">\r\n <form [formGroup]=\"loginForm()\" (submit)=\"login($event)\">\r\n <input class=\"form-control mb-4 radius-0\" type=\"email\" formControlName=\"username\"\r\n placeholder=\"Correo Electr\u00F3nico\">\r\n <input class=\"form-control mb-4 radius-0\" type=\"password\" formControlName=\"password\"\r\n placeholder=\"Contrase\u00F1a\">\r\n\r\n <div class=\"row d-flex flex-column\">\r\n <div class=\"col-12 mb-4\">\r\n <button type=\"submit\"\r\n class=\"bg-gray border-0 px-4 py-2 color-white ff-ubuntu-light\">INGRESAR</button>\r\n </div>\r\n <div class=\"col-12 d-flex justify-content-center align-items-center\">\r\n <a [routerLink]=\"'/auth/forgot-password'\" class=\"font-md ff-ubuntu-light\">\r\n \u00BFOlvid\u00F3 su contrase\u00F1a?\r\n </a>\r\n </div>\r\n </div>\r\n </form>\r\n </div>\r\n @if(loading){\r\n <app-loading-section-ec></app-loading-section-ec>\r\n }\r\n</div>", styles: [""], dependencies: [{ kind: "component", type: LoadingSectionEcComponent, selector: "app-loading-section-ec" }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$3.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }] });
7945
8266
  }
@@ -8039,6 +8360,7 @@ class RegisterFormEcComponent {
8039
8360
  _analyticsService = inject(AnalyticsService);
8040
8361
  _formBuilder = inject(FormBuilder);
8041
8362
  channelConfigService = inject(ChannelService);
8363
+ showPassword = false;
8042
8364
  /**
8043
8365
  * Indica si debe redireccionar o se queda en la misma pantalla
8044
8366
  */
@@ -8154,6 +8476,9 @@ class RegisterFormEcComponent {
8154
8476
  this.register_loading = false;
8155
8477
  }
8156
8478
  }
8479
+ togglePassword() {
8480
+ this.showPassword = !this.showPassword;
8481
+ }
8157
8482
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: RegisterFormEcComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
8158
8483
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.13", type: RegisterFormEcComponent, isStandalone: true, selector: "app-register-form-ec", inputs: { redirect: "redirect" }, outputs: { ready: "ready" }, ngImport: i0, template: "<div class=\"w-100 pl-md-5 position-relative\" id=\"register\">\r\n <div class=\"py-2\">\r\n <h5>CREAR CUENTA</h5>\r\n </div>\r\n <form id=\"registro\" [formGroup]=\"registerForm\" (submit)=\"register($event)\">\r\n <div class=\"form-group\">\r\n <label for=\"\" class=\"form-label\">NOMBRE</label>\r\n <input formControlName=\"firstName\" class=\"form-control rounded-0\" type=\"text\" placeholder=\"Nombre\">\r\n </div>\r\n <div class=\"form-group\">\r\n <label for=\"\" class=\"form-label\">APELLIDO</label>\r\n <input formControlName=\"lastName\" class=\"form-control rounded-0\" type=\"text\" placeholder=\"Apellido\">\r\n </div>\r\n <div class=\"form-group\">\r\n <label for=\"\" class=\"\">CORREO ELECTRONICO</label>\r\n <input formControlName=\"email\" email required class=\"form-control rounded-0\" type=\"email\"\r\n placeholder=\"Correo electr\u00F3nico\">\r\n </div>\r\n <div class=\"form-group\">\r\n <label for=\"\" class=\"form-label\">CONTRASE\u00D1A</label>\r\n <input formControlName=\"plainPassword\" required class=\"form-control rounded-0\" type=\"password\"\r\n placeholder=\"Contrase\u00F1a\">\r\n </div>\r\n <div class=\"form-group\">\r\n <label for=\"\" class=\"form-label\">REPETIR CONTRASE\u00D1A</label>\r\n <input formControlName=\"plainPassword2\" required class=\"form-control rounded-0\" type=\"password\"\r\n placeholder=\"Repetir contrase\u00F1a\">\r\n </div>\r\n\r\n <div class=\"custom-control d-flex flex-row form-check custom-checkbox mr-sm-2 mt-4 mb-2\">\r\n <input type=\"checkbox\" formControlName=\"terms\" required class=\"custom-control-input form-check-input\" name=\"Color2\"\r\n id=\"Color2\">\r\n <label class=\"custom-control-label ff-ubuntu-light font-sm form-check-label\" for=\"Color2\"> He\r\n le\u00EDdo y acepto las pol\u00EDticas de privacidad y los t\u00E9rminos y\r\n condiciones</label>\r\n </div>\r\n\r\n <div class=\"custom-control d-flex flex-row form-check custom-checkbox mr-sm-2 mb-4\">\r\n <input type=\"checkbox\" formControlName=\"newsletter\" class=\"custom-control-input form-check-input\" name=\"Color3\" id=\"Color3\">\r\n <label class=\"custom-control-label form-check-label ff-ubuntu-light font-sm\" for=\"Color3\">\r\n Suscripci\u00F3n al Newsletter</label>\r\n </div>\r\n\r\n <div class=\"row\">\r\n <div class=\"col-12\">\r\n <button [disabled]=\"registerForm.invalid\" type=\"submit\"\r\n class=\"btn btn-primary px-5 py-2 h-fit\">CREAR</button>\r\n </div>\r\n </div>\r\n </form>\r\n @if(loading){\r\n <app-loading-section-ec />\r\n }\r\n \r\n</div>\r\n", styles: [""], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$3.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$3.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$3.CheckboxRequiredValidator, selector: "input[type=checkbox][required][formControlName],input[type=checkbox][required][formControl],input[type=checkbox][required][ngModel]" }, { kind: "directive", type: i1$3.EmailValidator, selector: "[email][formControlName],[email][formControl],[email][ngModel]", inputs: ["email"] }, { kind: "directive", type: i1$3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$3.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: LoadingSectionEcComponent, selector: "app-loading-section-ec" }] });
8159
8484
  }
@@ -8308,29 +8633,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
8308
8633
  type: Output
8309
8634
  }] } });
8310
8635
 
8311
- class ComponentHelper {
8312
- constructor() {
8313
- this.ecOnConstruct();
8314
- }
8315
- ecOnInit = (params = {}) => {
8316
- };
8317
- ecOnConstruct = (params = {}) => {
8318
- };
8319
- hasParams = (params, searched) => {
8320
- if (!params || !searched)
8321
- return false;
8322
- const q = searched.trim().toLowerCase();
8323
- return params.some(p => {
8324
- const code = p?.['code']?.toString().toLowerCase() ?? '';
8325
- // Primero chequeo exacto, y si no, parcial
8326
- return code === q || code.includes(q);
8327
- });
8328
- };
8329
- navigateOnRouter(router, url) {
8330
- router.navigateByUrl(`/${url}`);
8331
- }
8332
- }
8333
-
8334
8636
  class PasswordResetEcComponent extends ComponentHelper {
8335
8637
  authService;
8336
8638
  toastr;
@@ -8931,6 +9233,7 @@ class CartItemEcComponent {
8931
9233
  _cartService = inject(CartService);
8932
9234
  _toastService = inject(ToastService);
8933
9235
  _constants = inject(CoreConstantsService);
9236
+ parametersService = inject(ParametersService);
8934
9237
  mediaUrl = this._constants.mediaUrl();
8935
9238
  quantity = 0;
8936
9239
  variantsToShow = ['TALLA', 'COLOR'];
@@ -9017,6 +9320,9 @@ class CartItemEcComponent {
9017
9320
  }
9018
9321
  return false; // Solo se ejecuta si no se cumple la condición
9019
9322
  }
9323
+ // PARAMETROS
9324
+ parameters$ = this.parametersService.getParameters();
9325
+ hasParams = this.parametersService.hasParams;
9020
9326
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: CartItemEcComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
9021
9327
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.13", type: CartItemEcComponent, isStandalone: true, selector: "app-cart-item-ec", inputs: { item: "item", inSidebar: "inSidebar" }, ngImport: i0, template: "@if(!inSidebar){\r\n<p>cart-item-ec works!</p>\r\n}@else{\r\n\r\n<div class=\"row\">\r\n <div class=\"col-3\">\r\n @let product= item.product;\r\n @if(item.variant_id && product.variants.length>0){\r\n <img [src]=\"mediaUrl + product.variants[0].images[0]\" alt=\"\" class=\"img-fluid\">\r\n }@else{\r\n <img [src]=\"mediaUrl + product.picturesdefault[0]\" alt=\"\" class=\"img-fluid\">\r\n }\r\n </div>\r\n <div class=\"col-7\">\r\n <div class=\"info d-flex flex-column align-items-start\">\r\n @if (item.product.special_mark?.length > 0 || item.product.saleprice) {\r\n <div class=\"marcas\">\r\n <img [src]=\"mediaUrl + (item.product.special_mark?.[0]?.images[0] || '')\" alt=\"\">\r\n\r\n @if (item.product.saleprice) {\r\n <div class=\"tag-dsc\">\r\n {{\r\n createDiscountMessage(item.product.saleprice,\r\n item.product.price)\r\n }}\r\n </div>\r\n }\r\n </div>\r\n }\r\n <a class=\"title text-dark text-decoration-none m-0 p-0 h6 mb-0\"\r\n [routerLink]=\"['/product', item.variant_id]\">{{\r\n item.product.name | titlecase\r\n }}</a>\r\n <div class=\"qty1\">\r\n <span>{{ item.product.id}}</span>\r\n </div>\r\n <div class=\"price h6 fw-bold mb-0 pb-0\">{{ item.product.price | ecCurrencySymbol\r\n }}</div>\r\n @if(getVariants(item); as options){\r\n <div class=\"d-flex align-items-center p-0\">\r\n @for(option of options; track $index){\r\n <span class=\"me-1\"> {{option.name | titlecase}}:</span>\r\n @if(option.name == 'COLOR'){\r\n <div class=\"p-2 rounded\" [style.background]=\"'#' + option.value\"></div>\r\n }@else{\r\n <b>{{option.value}}</b>\r\n }\r\n }\r\n </div>\r\n }\r\n <div class=\"campoCantidad mt-2\">\r\n <div class=\"numero\">\r\n <button (click)=\"less(item.product.variants[0]?.stock)\" class=\"btn btn-outline-secondary\"\r\n type=\"button\" id=\"button-addon1\">\r\n <i class=\"fa fa-minus\" aria-hidden=\"true\"></i>\r\n </button>\r\n <input type=\"text\" class=\"form-control text-center\" placeholder=\"\"\r\n aria-label=\"Example text with button addon\" aria-describedby=\"button-addon1\"\r\n [value]=\"item.quantity\" min=\"1\" step=\"1\" [(ngModel)]=\"quantity\"\r\n (change)=\"updateQuantity(item.product.variants[0]?.stock)\">\r\n <button (click)=\"plus(item.product.variants[0]?.stock)\" class=\"btn btn-outline-secondary\"\r\n type=\"button\" id=\"button-addon1\">\r\n <i class=\"fa fa-plus\" aria-hidden=\"true\"></i>\r\n </button>\r\n </div>\r\n\r\n </div>\r\n </div>\r\n </div>\r\n <div class=\"col-2\">\r\n <a (click)=\"deleteCartItem()\" class=\"btn botBorrar\"><i class=\"fa fa-trash\" aria-hidden=\"true\"></i></a>\r\n </div>\r\n</div>\r\n\r\n\r\n\r\n\r\n\r\n}", styles: [""], dependencies: [{ kind: "pipe", type: TitleCasePipe, name: "titlecase" }, { kind: "pipe", type: EcCurrencySymbolPipe, name: "ecCurrencySymbol" }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] });
9022
9328
  }
@@ -9539,16 +9845,33 @@ class MpRedirectEcComponent {
9539
9845
  ventana;
9540
9846
  window;
9541
9847
  localStorage;
9848
+ isMobile = false;
9849
+ sessionId = '';
9542
9850
  platformId = inject(PLATFORM_ID);
9543
9851
  constructor() {
9544
9852
  if (isPlatformBrowser(this.platformId)) {
9545
9853
  this.window = window;
9546
9854
  this.localStorage = localStorage;
9855
+ this.detectMobile();
9856
+ this.generateSessionId();
9547
9857
  }
9548
9858
  }
9549
9859
  ngOnInit() {
9550
9860
  this.getPreference();
9551
9861
  }
9862
+ detectMobile() {
9863
+ if (isPlatformBrowser(this.platformId) && this.window) {
9864
+ const userAgent = this.window.navigator.userAgent.toLowerCase();
9865
+ this.isMobile = /android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini|mobile/i.test(userAgent) ||
9866
+ (this.window.innerWidth <= 768);
9867
+ }
9868
+ }
9869
+ generateSessionId() {
9870
+ this.sessionId = `mp_session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
9871
+ if (isPlatformBrowser(this.platformId) && this.localStorage) {
9872
+ this.localStorage.setItem('mp_session_id', this.sessionId);
9873
+ }
9874
+ }
9552
9875
  clickClose = () => {
9553
9876
  /* this.closeModal = 'cancel'
9554
9877
  this.ventana?.close() */
@@ -9556,17 +9879,52 @@ class MpRedirectEcComponent {
9556
9879
  iniciar = () => {
9557
9880
  this.closeModal = '';
9558
9881
  this.clearStorageState();
9559
- this.ventana = this.window?.open(this.url);
9560
- this.callState();
9882
+ // Solo ejecutar en el browser
9883
+ if (!isPlatformBrowser(this.platformId)) {
9884
+ return;
9885
+ }
9886
+ // En mobile, marcamos esta pestaña como la principal
9887
+ if (this.isMobile && this.localStorage) {
9888
+ this.localStorage.setItem('mp_main_tab', this.sessionId);
9889
+ this.localStorage.setItem('mp_payment_started', 'true');
9890
+ }
9891
+ if (this.isMobile && this.window) {
9892
+ // En mobile, usar window.location.href en lugar de popup
9893
+ this.window.location.href = this.url;
9894
+ }
9895
+ else if (this.window) {
9896
+ // En desktop, usar popup como antes
9897
+ this.ventana = this.window.open(this.url);
9898
+ this.callState();
9899
+ }
9561
9900
  };
9562
9901
  callState = () => {
9902
+ // Solo ejecutar en el browser
9903
+ if (!isPlatformBrowser(this.platformId)) {
9904
+ return;
9905
+ }
9563
9906
  let state = this.closeModal != '' ? this.closeModal : this.localStorage?.getItem('state');
9564
- !state && this.ventana.closed && (state = 'cancel');
9907
+ !state && this.ventana?.closed && (state = 'cancel');
9565
9908
  this.loading = true;
9566
9909
  state && console.log(state);
9910
+ // En mobile, verificar si esta pestaña debe procesar el estado
9911
+ if (this.isMobile && this.localStorage) {
9912
+ const mainTab = this.localStorage.getItem('mp_main_tab');
9913
+ const currentSessionId = this.localStorage.getItem('mp_session_id');
9914
+ // Si no es la pestaña principal, no procesar
9915
+ if (mainTab !== currentSessionId && mainTab !== null) {
9916
+ console.log('Esta no es la pestaña principal, ignorando estado');
9917
+ return;
9918
+ }
9919
+ }
9567
9920
  if (state) {
9568
9921
  this.loading = false;
9569
9922
  this.localStorage?.removeItem('state');
9923
+ // Limpiar marcadores de mobile
9924
+ if (this.isMobile && this.localStorage) {
9925
+ this.localStorage.removeItem('mp_main_tab');
9926
+ this.localStorage.removeItem('mp_payment_started');
9927
+ }
9570
9928
  if (state == 'success') {
9571
9929
  this.ventana?.close();
9572
9930
  this.ready.emit(true);
@@ -9591,17 +9949,31 @@ class MpRedirectEcComponent {
9591
9949
  this.processError('');
9592
9950
  return;
9593
9951
  }
9594
- setTimeout(() => {
9595
- this.callState();
9596
- }, 5000);
9952
+ // Solo continuar polling si no es mobile o si es la pestaña principal
9953
+ if (!this.isMobile || (this.isMobile && this.localStorage?.getItem('mp_main_tab') === this.sessionId)) {
9954
+ setTimeout(() => {
9955
+ this.callState();
9956
+ }, 5000);
9957
+ }
9597
9958
  };
9598
9959
  processError = (err) => {
9599
9960
  this._toastService.show(err != '' ? err : 'payment-error');
9600
9961
  // console.log("ERROR ENVIO BACK ", err);
9601
9962
  };
9602
9963
  clearStorageState = () => {
9603
- sessionStorage.removeItem('state');
9964
+ // Solo ejecutar en el browser
9965
+ if (!isPlatformBrowser(this.platformId)) {
9966
+ return;
9967
+ }
9968
+ if (typeof sessionStorage !== 'undefined') {
9969
+ sessionStorage.removeItem('state');
9970
+ }
9604
9971
  this.localStorage?.removeItem('state');
9972
+ // Limpiar también los marcadores de mobile
9973
+ if (this.isMobile && this.localStorage) {
9974
+ this.localStorage.removeItem('mp_processing_tab');
9975
+ this.localStorage.removeItem('mp_processing_tab_time');
9976
+ }
9605
9977
  };
9606
9978
  getPreference = () => this._paymentService.getPreference(this.allData).then(res => {
9607
9979
  this.preference = res;
@@ -9612,7 +9984,11 @@ class MpRedirectEcComponent {
9612
9984
  //this.error = message;
9613
9985
  };
9614
9986
  renderMP = (preference) => {
9615
- this.window?.addEventListener("message", (event) => {
9987
+ // Solo ejecutar en el browser
9988
+ if (!isPlatformBrowser(this.platformId) || !this.window) {
9989
+ return;
9990
+ }
9991
+ this.window.addEventListener("message", (event) => {
9616
9992
  if (event.origin !== 'https://www.mercadopago.com.ar' || !event.data.type) {
9617
9993
  return;
9618
9994
  }
@@ -10493,11 +10869,9 @@ class RelatedProductsEcComponent extends BlockEcComponent {
10493
10869
  this._relatedProductsSubject.next(relatedProducts);
10494
10870
  res.map((products) => this._analyticsService.callEvent('view_item_list', { products: products.items, item_list_name: products.title || 'Related Products', item_list_id: products.id || 'related-products' }));
10495
10871
  // Inicializar swiper después de que los datos estén disponibles
10496
- if (isPlatformBrowser(this.platformId)) {
10497
- setTimeout(() => {
10498
- this.initSwiper();
10499
- }, 100);
10500
- }
10872
+ setTimeout(() => {
10873
+ this.initSwiper();
10874
+ }, 100);
10501
10875
  });
10502
10876
  }
10503
10877
  initSwiper() {
@@ -11749,5 +12123,5 @@ const directives = [
11749
12123
  * Generated bundle index. Do not edit.
11750
12124
  */
11751
12125
 
11752
- export { AccountEcComponent, AddressingService, AnalyticsService, AuthEcComponent, AuthService, AuthStorageService, BlockBannerBoxEcComponent, BlockBannerFullEcComponent, BlockFormContactEcComponent, BlockHtmlEcComponent, BlockNewsletterEcComponent, BlockProductsEcComponent, BlocksEcComponent, BlocksRepositoryService, BlocksService, BreadcrumbEcComponent, CartEcComponent, CartItemEcComponent, CartService, ChannelService, CheckoutEcComponent, CheckoutService, CollectionEcComponent, ConfirmAccountEcComponent, ContactEcComponent, CoreConstantsService, CouponEcComponent, CurrencyService, DopplerService, ENVIRONMENT_TOKEN, EcCurrencySymbolPipe, EcSafeHtmlPipe, FacebookPixelService, FaqsEcComponent, FiltersEcComponent, FiltersService, FiltersSortEcComponent, FooterEcComponent, ForgotPasswordEcComponent, FormService, GTMService, GoogleAnalyticsService, HeaderEcComponent, HomeEcComponent, LoadingFullEcComponent, LoadingInlineEcComponent, LoadingSectionEcComponent, LocalStorageService, LoginFormEcComponent, MagnizoomEcComponent, MetricoolPixelService, NgxLocalStorageService, OptionsService, OrderEcComponent, OrderUtilityService, OrdersListEcComponent, OrdersService, PaginationService, ParametersService, ParamsContext, PasswordResetEcComponent, PaymentService, PriceEcComponent, PriceRangeFilterComponent, ProductDetailEcComponent, ProductDetailService, ProductEcComponent, ProductOffDirective, ProductStockDirective, ProductsService, ReCaptchaEcComponent, ReCaptchaService, RegisterFormEcComponent, RegisterWholesalerFormEcComponent, RelatedProductsEcComponent, ReviewsEcComponent, ReviewsFormEcComponent, SectionContainerEcComponent, ShareEcComponent, ShipmentService, SidebarEcComponent, StoresEcComponent, SuccessEcComponent, TestService, ToastService, VariantsEcComponent, authGuard, authInterceptor, directives, provideEnvironment };
12126
+ export { AccountEcComponent, AddressingService, AnalyticsService, AuthEcComponent, AuthService, AuthStorageService, BlockBannerBoxEcComponent, BlockBannerFullEcComponent, BlockFormContactEcComponent, BlockHtmlEcComponent, BlockNewsletterEcComponent, BlockProductsEcComponent, BlocksEcComponent, BlocksRepositoryService, BlocksService, BreadcrumbEcComponent, CartEcComponent, CartItemEcComponent, CartService, ChannelService, CheckoutEcComponent, CheckoutService, CollectionEcComponent, ConfirmAccountEcComponent, ContactEcComponent, CoreConstantsService, CouponEcComponent, CurrencyService, DopplerService, ENVIRONMENT_TOKEN, EcCurrencySymbolPipe, EcSafeHtmlPipe, FacebookPixelService, FaqsEcComponent, FiltersEcComponent, FiltersService, FiltersSortEcComponent, FooterEcComponent, ForgotPasswordEcComponent, FormService, GTMService, GoogleAnalyticsService, HeaderEcComponent, HomeEcComponent, LoadingFullEcComponent, LoadingInlineEcComponent, LoadingSectionEcComponent, LocalStorageService, LoginFormEcComponent, MagnizoomEcComponent, MetricoolPixelService, NgxLocalStorageService, OptionsService, OrderEcComponent, OrderUtilityService, OrdersListEcComponent, OrdersService, PaginationService, ParametersService, ParamsContext, PasswordResetEcComponent, PaymentService, PriceEcComponent, PriceRangeFilterComponent, ProductDetailEcComponent, ProductDetailService, ProductEcComponent, ProductOffDirective, ProductStockDirective, ProductsService, ReCaptchaEcComponent, ReCaptchaService, RedsysCatchEcComponent, RegisterFormEcComponent, RegisterWholesalerFormEcComponent, RelatedProductsEcComponent, ReviewsEcComponent, ReviewsFormEcComponent, SectionContainerEcComponent, ShareEcComponent, ShipmentService, SidebarEcComponent, StoresEcComponent, SuccessEcComponent, TestService, ToastService, VariantsEcComponent, authGuard, authInterceptor, directives, provideEnvironment };
11753
12127
  //# sourceMappingURL=ng-easycommerce-v18.mjs.map