ng-easycommerce-v18 0.3.14-beta.3 → 0.3.14-beta.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/README.md +18 -0
  2. package/esm2022/lib/constants/api.constants.service.mjs +1 -7
  3. package/esm2022/lib/ec-components/abstractions-components/menu-ec.component.mjs +17 -1
  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/cart-ec/cart-item-ec/cart-item-ec.component.mjs +6 -2
  7. package/esm2022/lib/ec-components/checkout-ec/payment-ec/payment-methods/mp-redirect-ec/mp-redirect-ec.component.mjs +141 -75
  8. package/esm2022/lib/ec-components/filters-ec/filters-ec.component.mjs +30 -5
  9. package/esm2022/lib/ec-components/header-ec/header-ec.component.mjs +23 -3
  10. package/esm2022/lib/ec-components/product-ec/product-ec.component.mjs +95 -5
  11. package/esm2022/lib/ec-components/widgets-ec/index.mjs +2 -1
  12. package/esm2022/lib/ec-components/widgets-ec/redsys-catch-ec/redsys-catch-ec.component.mjs +193 -0
  13. package/esm2022/lib/interfaces/filter.mjs +1 -1
  14. package/esm2022/lib/interfaces/options.mjs +1 -1
  15. package/fesm2022/ng-easycommerce-v18.mjs +517 -112
  16. package/fesm2022/ng-easycommerce-v18.mjs.map +1 -1
  17. package/lib/ec-components/abstractions-components/menu-ec.component.d.ts +12 -0
  18. package/lib/ec-components/auth-ec/login-form-ec/login-form-ec.component.d.ts +2 -0
  19. package/lib/ec-components/auth-ec/register-form-ec/register-form-ec.component.d.ts +2 -0
  20. package/lib/ec-components/cart-ec/cart-item-ec/cart-item-ec.component.d.ts +5 -0
  21. package/lib/ec-components/checkout-ec/payment-ec/payment-methods/mp-redirect-ec/mp-redirect-ec.component.d.ts +38 -16
  22. package/lib/ec-components/filters-ec/filters-ec.component.d.ts +12 -4
  23. package/lib/ec-components/header-ec/header-ec.component.d.ts +5 -1
  24. package/lib/ec-components/product-ec/product-ec.component.d.ts +10 -1
  25. package/lib/ec-components/widgets-ec/index.d.ts +1 -0
  26. package/lib/ec-components/widgets-ec/redsys-catch-ec/redsys-catch-ec.component.d.ts +47 -0
  27. package/lib/interfaces/filter.d.ts +1 -0
  28. package/lib/interfaces/options.d.ts +2 -0
  29. package/package.json +1 -1
@@ -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';
@@ -87,14 +87,8 @@ class ApiConstantsService {
87
87
  }
88
88
  // 2️⃣ Runtime (SSR / Node)
89
89
  if (isPlatformServer(this.platformId)) {
90
- // Prioriza la inyección del token API_URL
91
90
  if (this.ssrApiUrl)
92
91
  return this.ssrApiUrl;
93
- // Si no se inyectó, toma del entorno
94
- if (isPlatformServer(this.platformId)) {
95
- if (this.ssrApiUrl)
96
- return this.ssrApiUrl;
97
- }
98
92
  }
99
93
  // 3️⃣ Fallback (environment de Angular)
100
94
  return this.environment.apiUrl ?? '';
@@ -6063,6 +6057,22 @@ class MenuEcComponent {
6063
6057
  hasVisibleProperty(category) {
6064
6058
  return category.isVisible === true;
6065
6059
  }
6060
+ filterVisibleTree(list) {
6061
+ return (list ?? [])
6062
+ .filter((n) => n?.isVisible === true)
6063
+ .map(n => ({ ...n, children: this.filterVisibleTree(n.children) }));
6064
+ }
6065
+ // Exponé streams ya filtrados
6066
+ categoriesVisible$ = this.categories$.pipe(map((list) => this.filterVisibleTree(list)));
6067
+ sectionsVisible$ = this.sections$.pipe(map((list) => this.filterVisibleTree(list)));
6068
+ attributesVisible$ = this.attributes$.pipe(map((list) => this.filterVisibleTree(list)));
6069
+ // Helpers de conveniencia opcionales
6070
+ getVisibleChildren(node) {
6071
+ return this.filterVisibleTree(node?.children);
6072
+ }
6073
+ hasVisibleChildren(node) {
6074
+ return this.getVisibleChildren(node).length > 0;
6075
+ }
6066
6076
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MenuEcComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
6067
6077
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: MenuEcComponent, isStandalone: true, selector: "lib-footer-ec", ngImport: i0, template: '<p>Menu and Footer Helper Component</p>', isInline: true });
6068
6078
  }
@@ -6209,6 +6219,11 @@ class HeaderEcComponent extends MenuEcComponent {
6209
6219
  hidePrices = false;
6210
6220
  __authService = inject(AuthService);
6211
6221
  _channelService = inject(ChannelService);
6222
+ changeDetector = inject(ChangeDetectorRef);
6223
+ appRouter = inject(Router);
6224
+ platformId = inject(PLATFORM_ID);
6225
+ // Observable del estado de autenticación
6226
+ logged$;
6212
6227
  isAuthenticated$ = this.__authService.isAuthenticated();
6213
6228
  constructor() {
6214
6229
  super();
@@ -6222,11 +6237,26 @@ class HeaderEcComponent extends MenuEcComponent {
6222
6237
  coreConstantsService = inject(CoreConstantsService);
6223
6238
  router = inject(Router);
6224
6239
  cdr = inject(ChangeDetectorRef); // Inyectamos ChangeDetectorRef para forzar la actualización
6225
- platformId = inject(PLATFORM_ID);
6226
6240
  ngOnInit() {
6227
6241
  this.channel = this.coreConstantsService.getChannel();
6228
6242
  this.onWindowScroll();
6229
6243
  this.detectRouteChange(); // Llamamos a la función que detecta el cambio de ruta
6244
+ // Usar el Observable del AuthService
6245
+ this.logged$ = this.__authService.loggedIn$;
6246
+ // Suscribirse al Observable y forzar detección de cambios cuando sea necesario
6247
+ this.logged$.subscribe(isLoggedIn => {
6248
+ this.changeDetector.detectChanges();
6249
+ });
6250
+ if (isPlatformBrowser(this.platformId)) {
6251
+ this.appRouter.events.subscribe(evt => {
6252
+ if (evt instanceof NavigationEnd) {
6253
+ // Forzar detección de cambios después de navegación
6254
+ setTimeout(() => {
6255
+ this.changeDetector.detectChanges();
6256
+ }, 100);
6257
+ }
6258
+ });
6259
+ }
6230
6260
  }
6231
6261
  ngAfterViewInit() {
6232
6262
  this.setupMobileMenu(); // Inicializamos el menú móvil
@@ -6940,6 +6970,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
6940
6970
  class ProductEcComponent {
6941
6971
  injector = inject(Injector);
6942
6972
  routerService = inject(Router);
6973
+ _cartService = inject(CartService);
6974
+ _toastService = inject(ToastService);
6975
+ isAddingToCart = signal(false);
6976
+ quantity = signal(1);
6943
6977
  /**
6944
6978
  * Navega al detalle del producto y sube al inicio de la página
6945
6979
  * @param productId ID del producto
@@ -7012,12 +7046,97 @@ class ProductEcComponent {
7012
7046
  const discount = ((originalPrice - salePrice) / originalPrice) * 100;
7013
7047
  return Math.round(discount);
7014
7048
  }
7049
+ plus(stock, multipleQuantity) {
7050
+ console.log('Aumentando cantidad');
7051
+ const current = Number(this.quantity()); // <-- fuerza a número
7052
+ if (multipleQuantity && multipleQuantity > 0) {
7053
+ stock
7054
+ ? (current < stock
7055
+ ? this.quantity.set(current + multipleQuantity)
7056
+ : this._toastService.show('out-of-stock-actually'))
7057
+ : this.quantity.set(current + multipleQuantity);
7058
+ }
7059
+ else {
7060
+ stock
7061
+ ? (current < stock
7062
+ ? this.quantity.set(current + 1)
7063
+ : this._toastService.show('out-of-stock-actually'))
7064
+ : this.quantity.set(current + 1);
7065
+ }
7066
+ }
7067
+ less(multipleQuantity) {
7068
+ console.log('Disminuyendo cantidad');
7069
+ const current = Number(this.quantity()); // <-- fuerza a número
7070
+ if (multipleQuantity && multipleQuantity > 0) {
7071
+ current > multipleQuantity
7072
+ ? this.quantity.set(current - multipleQuantity)
7073
+ : null;
7074
+ }
7075
+ else {
7076
+ current > 1 ? this.quantity.set(current - 1) : null;
7077
+ }
7078
+ }
7079
+ addToCart() {
7080
+ console.log('Añadiendo al carrito');
7081
+ if (this.isAddingToCart() || this.quantity() <= 0)
7082
+ return;
7083
+ // Verificar que tenemos el producto y sus variantes
7084
+ if (!this.product || !this.product.variants || this.product.variants.length === 0) {
7085
+ this._toastService.show('cant-buy');
7086
+ return;
7087
+ }
7088
+ // Verificar stock
7089
+ const firstVariant = this.product.variants[0];
7090
+ if (!firstVariant.stock || firstVariant.stock <= 0) {
7091
+ this._toastService.show('out-of-stock');
7092
+ return;
7093
+ }
7094
+ // Verificar que no se supere el stock disponible
7095
+ if (this.quantity() > firstVariant.stock) {
7096
+ this._toastService.show('out-of-stock-actually');
7097
+ return;
7098
+ }
7099
+ this.isAddingToCart.set(true);
7100
+ try {
7101
+ // Agregar al carrito usando CartService directamente
7102
+ this._cartService.addToCart(this.product, this.quantity(), firstVariant.code);
7103
+ console.log('Producto agregado al carrito exitosamente');
7104
+ // Resetear cantidad a 1 después de agregar al carrito
7105
+ this.quantity.set(1);
7106
+ }
7107
+ catch (error) {
7108
+ console.error('Error al agregar al carrito:', error);
7109
+ this._toastService.show('cant-buy');
7110
+ }
7111
+ setTimeout(() => {
7112
+ this.isAddingToCart.set(false);
7113
+ }, 2000);
7114
+ }
7115
+ checkStock(stock) {
7116
+ if (this.quantity() >= stock)
7117
+ this.quantity.set(stock);
7118
+ }
7119
+ onQuantityChange(event) {
7120
+ const target = event.target;
7121
+ const value = +target.value;
7122
+ // Validar que el valor sea mayor a 0
7123
+ if (value > 0) {
7124
+ this.quantity.set(value);
7125
+ // Validar contra el stock disponible
7126
+ const maxStock = this.product?.variants?.[0]?.stock || this.product?.stock || 0;
7127
+ this.checkStock(maxStock);
7128
+ }
7129
+ else {
7130
+ // Si el valor es 0 o negativo, resetear a 1
7131
+ this.quantity.set(1);
7132
+ }
7133
+ }
7015
7134
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ProductEcComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
7016
- 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" }] });
7135
+ 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 }] });
7017
7136
  }
7018
7137
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ProductEcComponent, decorators: [{
7019
7138
  type: Component,
7020
- 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>" }]
7139
+ 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>" }]
7021
7140
  }], ctorParameters: () => [], propDecorators: { product: [{
7022
7141
  type: Input,
7023
7142
  args: [{
@@ -7595,6 +7714,212 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
7595
7714
  args: ['mainCanvas', { static: true }]
7596
7715
  }] } });
7597
7716
 
7717
+ class ComponentHelper {
7718
+ constructor() {
7719
+ this.ecOnConstruct();
7720
+ }
7721
+ ecOnInit = (params = {}) => {
7722
+ };
7723
+ ecOnConstruct = (params = {}) => {
7724
+ };
7725
+ hasParams = (params, searched) => {
7726
+ if (!params || !searched)
7727
+ return false;
7728
+ const q = searched.trim().toLowerCase();
7729
+ return params.some(p => {
7730
+ const code = p?.['code']?.toString().toLowerCase() ?? '';
7731
+ // Primero chequeo exacto, y si no, parcial
7732
+ return code === q || code.includes(q);
7733
+ });
7734
+ };
7735
+ navigateOnRouter(router, url) {
7736
+ router.navigateByUrl(`/${url}`);
7737
+ }
7738
+ }
7739
+
7740
+ /**
7741
+ * Catch genérico para redirecciones de pagos.
7742
+ * - Normaliza el estado recibido por params/query.
7743
+ * - Informa el resultado al opener (postMessage), BroadcastChannel y localStorage.
7744
+ * - Intenta cerrarse; si el cierre falla, redirige según el estado.
7745
+ */
7746
+ class RedsysCatchEcComponent extends ComponentHelper {
7747
+ activedRoute;
7748
+ router;
7749
+ checkoutService;
7750
+ renderer;
7751
+ elementRef;
7752
+ document;
7753
+ platformId;
7754
+ message = '';
7755
+ subscription = null;
7756
+ sid = '';
7757
+ bc;
7758
+ constructor(activedRoute, router, checkoutService, renderer, elementRef, document, platformId) {
7759
+ super();
7760
+ this.activedRoute = activedRoute;
7761
+ this.router = router;
7762
+ this.checkoutService = checkoutService;
7763
+ this.renderer = renderer;
7764
+ this.elementRef = elementRef;
7765
+ this.document = document;
7766
+ this.platformId = platformId;
7767
+ this.hideHeaderFooter();
7768
+ this.ecOnConstruct();
7769
+ }
7770
+ ngOnInit() {
7771
+ if (isPlatformBrowser(this.platformId) && 'BroadcastChannel' in window) {
7772
+ this.bc = new BroadcastChannel('mp_payment');
7773
+ }
7774
+ this.subscription = combineLatest([this.activedRoute.params, this.activedRoute.queryParams])
7775
+ .subscribe(([routeParams, q]) => {
7776
+ let stateStr = routeParams['state'];
7777
+ if (stateStr === 'statuspayment')
7778
+ stateStr = q['status'];
7779
+ const statusParam = (stateStr || q['status'] || q['state'] || '').toString();
7780
+ const state = this.normalizeState(statusParam);
7781
+ this.sid = (q['sid'] || (isPlatformBrowser(this.platformId) ? localStorage.getItem('mp:sid') : '') || '').toString();
7782
+ this.storeTotalAmount(q);
7783
+ this.setStateInLocal('Su pago fue procesado con éxito.', state);
7784
+ this.signalState(state);
7785
+ this.tryCloseSelf(() => {
7786
+ const target = (state === 'success' || state === 'pending')
7787
+ ? ['/checkout/order_success']
7788
+ : ['/checkout'];
7789
+ setTimeout(() => this.router.navigate(target), 4500);
7790
+ });
7791
+ });
7792
+ this.ecOnInit();
7793
+ }
7794
+ ngOnDestroy() {
7795
+ this.subscription?.unsubscribe();
7796
+ this.bc?.close();
7797
+ this.showHeaderFooter();
7798
+ }
7799
+ /** Guarda total_amount si viene desde el PSP. */
7800
+ storeTotalAmount(queryParams) {
7801
+ const totalAmount = queryParams['total_amount'];
7802
+ if (totalAmount && isPlatformBrowser(this.platformId)) {
7803
+ localStorage.setItem('total_amount', totalAmount);
7804
+ }
7805
+ }
7806
+ /** Setea mensaje y estado en storages. */
7807
+ setStateInLocal = (mensaje, state) => {
7808
+ this.message = mensaje;
7809
+ if (!isPlatformBrowser(this.platformId))
7810
+ return;
7811
+ try {
7812
+ localStorage.setItem('state', state);
7813
+ }
7814
+ catch { }
7815
+ try {
7816
+ sessionStorage.setItem('modalnews', 'false');
7817
+ }
7818
+ catch { }
7819
+ };
7820
+ /** Normaliza estados heterogéneos de distintos gateways. */
7821
+ normalizeState(raw) {
7822
+ const v = (raw || '').toLowerCase();
7823
+ if (v === '200' || v === 'ok' || v === 'success')
7824
+ return 'success';
7825
+ if (v === 'pending')
7826
+ return 'pending';
7827
+ if (v === 'cancel')
7828
+ return 'cancel';
7829
+ return 'failure'; // failure, 0, error, rejected, chargeback, desconocidos
7830
+ }
7831
+ /**
7832
+ * Informa el resultado: postMessage al opener, BroadcastChannel y localStorage
7833
+ * (este último permite polling en la pestaña madre).
7834
+ */
7835
+ signalState(state) {
7836
+ if (!isPlatformBrowser(this.platformId))
7837
+ return;
7838
+ const sid = this.sid || localStorage.getItem('mp:sid') || '';
7839
+ const payload = { type: 'mp:state', sid, state };
7840
+ try {
7841
+ window.opener && window.opener.postMessage(payload, '*');
7842
+ }
7843
+ catch { }
7844
+ try {
7845
+ this.bc?.postMessage(payload);
7846
+ }
7847
+ catch { }
7848
+ try {
7849
+ localStorage.setItem(`mp:state:${sid}`, state);
7850
+ }
7851
+ catch { }
7852
+ try {
7853
+ localStorage.setItem('state', state);
7854
+ }
7855
+ catch { }
7856
+ }
7857
+ /** Intenta cerrar la pestaña actual; si falla, ejecuta el callback de fallback. */
7858
+ tryCloseSelf(onFail) {
7859
+ if (!isPlatformBrowser(this.platformId))
7860
+ return onFail();
7861
+ let attempted = false;
7862
+ try {
7863
+ window.close();
7864
+ attempted = true;
7865
+ }
7866
+ catch { }
7867
+ if (!attempted)
7868
+ onFail();
7869
+ }
7870
+ /** Oculta header/footer para esta pantalla mínima. */
7871
+ hideHeaderFooter() {
7872
+ if (!isPlatformBrowser(this.platformId))
7873
+ return;
7874
+ const header = this.document.querySelector('header');
7875
+ const footer = this.document.querySelector('footer');
7876
+ if (header)
7877
+ this.renderer.setStyle(header, 'display', 'none');
7878
+ if (footer)
7879
+ this.renderer.setStyle(footer, 'display', 'none');
7880
+ }
7881
+ /** Restaura header/footer al salir. */
7882
+ showHeaderFooter() {
7883
+ if (!isPlatformBrowser(this.platformId))
7884
+ return;
7885
+ const header = this.document.querySelector('header');
7886
+ const footer = this.document.querySelector('footer');
7887
+ if (header)
7888
+ this.renderer.removeStyle(header, 'display');
7889
+ if (footer)
7890
+ this.renderer.removeStyle(footer, 'display');
7891
+ }
7892
+ setStateInSession(mensaje, state) {
7893
+ this.message = mensaje;
7894
+ if (!isPlatformBrowser(this.platformId))
7895
+ return;
7896
+ try {
7897
+ sessionStorage.setItem('state', state);
7898
+ }
7899
+ catch { }
7900
+ try {
7901
+ localStorage.setItem('state', state);
7902
+ }
7903
+ catch { }
7904
+ try {
7905
+ sessionStorage.setItem('modalnews', 'false');
7906
+ }
7907
+ catch { }
7908
+ }
7909
+ 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 });
7910
+ 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" }] });
7911
+ }
7912
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: RedsysCatchEcComponent, decorators: [{
7913
+ type: Component,
7914
+ 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"] }]
7915
+ }], ctorParameters: () => [{ type: i2.ActivatedRoute }, { type: i2.Router }, { type: CheckoutService }, { type: i0.Renderer2 }, { type: i0.ElementRef }, { type: Document, decorators: [{
7916
+ type: Inject,
7917
+ args: [DOCUMENT]
7918
+ }] }, { type: undefined, decorators: [{
7919
+ type: Inject,
7920
+ args: [PLATFORM_ID]
7921
+ }] }] });
7922
+
7598
7923
  // export * from './rating-ec/rating-ec.component';
7599
7924
 
7600
7925
  class BlockFormContactEcComponent extends BlockEcComponent {
@@ -7870,6 +8195,7 @@ class LoginFormEcComponent {
7870
8195
  _formBuilder = inject(FormBuilder);
7871
8196
  _toastService = inject(ToastService);
7872
8197
  _router = inject(Router);
8198
+ showPassword = false;
7873
8199
  /**
7874
8200
  * Parametro para indicar si tras loguear
7875
8201
  * debe redireccionar o no.
@@ -7958,6 +8284,9 @@ class LoginFormEcComponent {
7958
8284
  ? resolverFunction()
7959
8285
  : this._router.navigateByUrl(this.redirectTo);
7960
8286
  }
8287
+ togglePassword() {
8288
+ this.showPassword = !this.showPassword;
8289
+ }
7961
8290
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: LoginFormEcComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
7962
8291
  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"] }] });
7963
8292
  }
@@ -8057,6 +8386,7 @@ class RegisterFormEcComponent {
8057
8386
  _analyticsService = inject(AnalyticsService);
8058
8387
  _formBuilder = inject(FormBuilder);
8059
8388
  channelConfigService = inject(ChannelService);
8389
+ showPassword = false;
8060
8390
  /**
8061
8391
  * Indica si debe redireccionar o se queda en la misma pantalla
8062
8392
  */
@@ -8172,6 +8502,9 @@ class RegisterFormEcComponent {
8172
8502
  this.register_loading = false;
8173
8503
  }
8174
8504
  }
8505
+ togglePassword() {
8506
+ this.showPassword = !this.showPassword;
8507
+ }
8175
8508
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: RegisterFormEcComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
8176
8509
  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" }] });
8177
8510
  }
@@ -8326,29 +8659,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
8326
8659
  type: Output
8327
8660
  }] } });
8328
8661
 
8329
- class ComponentHelper {
8330
- constructor() {
8331
- this.ecOnConstruct();
8332
- }
8333
- ecOnInit = (params = {}) => {
8334
- };
8335
- ecOnConstruct = (params = {}) => {
8336
- };
8337
- hasParams = (params, searched) => {
8338
- if (!params || !searched)
8339
- return false;
8340
- const q = searched.trim().toLowerCase();
8341
- return params.some(p => {
8342
- const code = p?.['code']?.toString().toLowerCase() ?? '';
8343
- // Primero chequeo exacto, y si no, parcial
8344
- return code === q || code.includes(q);
8345
- });
8346
- };
8347
- navigateOnRouter(router, url) {
8348
- router.navigateByUrl(`/${url}`);
8349
- }
8350
- }
8351
-
8352
8662
  class PasswordResetEcComponent extends ComponentHelper {
8353
8663
  authService;
8354
8664
  toastr;
@@ -8490,6 +8800,8 @@ class FiltersEcComponent {
8490
8800
  console.error('Filter or selected element is undefined:', { filter, selected });
8491
8801
  return;
8492
8802
  }
8803
+ if (selected.isVisible !== true)
8804
+ return;
8493
8805
  if (typeof filter.setSelected !== 'function') {
8494
8806
  console.error('filter.setSelected is not a function. Filter might not be an instance of the expected class:', filter);
8495
8807
  return;
@@ -8559,13 +8871,36 @@ class FiltersEcComponent {
8559
8871
  }) ?? false;
8560
8872
  }
8561
8873
  /**
8562
- * Verifica si una categoría tiene la propiedad isVisible y está marcada como visible
8563
- * @param category - La categoría a verificar
8564
- * @returns true si la categoría es visible, false en caso contrario
8565
- */
8874
+ * Verifica si una categoría tiene la propiedad isVisible y está marcada como visible
8875
+ * @param category - La categoría a verificar
8876
+ * @returns true si la categoría es visible, false en caso contrario
8877
+ */
8566
8878
  hasVisibleProperty(category) {
8567
8879
  return category.isVisible === true;
8568
8880
  }
8881
+ /** Lista visible (filtra recursivo por isVisible) */
8882
+ getVisibleData(filter) {
8883
+ if (!filter)
8884
+ return [];
8885
+ return this.filterVisibleTree(filter.data);
8886
+ }
8887
+ /** Children visibles de un nodo */
8888
+ getVisibleChildren(node) {
8889
+ return this.filterVisibleTree(node?.children ?? []);
8890
+ }
8891
+ /** Tiene hijos visibles? */
8892
+ hasVisibleChildren(node) {
8893
+ return this.getVisibleChildren(node).length > 0;
8894
+ }
8895
+ /** Utilidad recursiva */
8896
+ filterVisibleTree(list = []) {
8897
+ return (list ?? [])
8898
+ .filter(n => n.isVisible === true)
8899
+ .map(n => ({
8900
+ ...n,
8901
+ children: this.filterVisibleTree(n.children ?? [])
8902
+ }));
8903
+ }
8569
8904
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: FiltersEcComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
8570
8905
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: FiltersEcComponent, isStandalone: true, selector: "lib-filters-ec", inputs: { setSelect: "setSelect" }, ngImport: i0, template: "<p>filters-ec works!</p>\r\n", styles: [""] });
8571
8906
  }
@@ -8949,6 +9284,7 @@ class CartItemEcComponent {
8949
9284
  _cartService = inject(CartService);
8950
9285
  _toastService = inject(ToastService);
8951
9286
  _constants = inject(CoreConstantsService);
9287
+ parametersService = inject(ParametersService);
8952
9288
  mediaUrl = this._constants.mediaUrl();
8953
9289
  quantity = 0;
8954
9290
  variantsToShow = ['TALLA', 'COLOR'];
@@ -9035,6 +9371,9 @@ class CartItemEcComponent {
9035
9371
  }
9036
9372
  return false; // Solo se ejecuta si no se cumple la condición
9037
9373
  }
9374
+ // PARAMETROS
9375
+ parameters$ = this.parametersService.getParameters();
9376
+ hasParams = this.parametersService.hasParams;
9038
9377
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: CartItemEcComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
9039
9378
  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"] }] });
9040
9379
  }
@@ -9546,108 +9885,174 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
9546
9885
  class MpRedirectEcComponent {
9547
9886
  _paymentService = inject(PaymentService);
9548
9887
  _toastService = inject(ToastService);
9888
+ platformId = inject(PLATFORM_ID);
9889
+ finished = false;
9549
9890
  method = null;
9550
9891
  total_amount = 0;
9551
9892
  allData;
9552
9893
  ready = new EventEmitter();
9553
9894
  preference;
9554
- loading = false;
9555
9895
  url;
9556
- closeModal = '';
9557
- ventana;
9558
- window;
9559
- localStorage;
9560
- platformId = inject(PLATFORM_ID);
9561
- constructor() {
9896
+ // Fases de UI
9897
+ phase = 'idle';
9898
+ get isIdle() { return this.phase === 'idle'; }
9899
+ get isPaying() { return this.phase === 'paying'; }
9900
+ get isFinalizing() { return this.phase === 'finalizing'; }
9901
+ ventana = null;
9902
+ windowRef;
9903
+ sid = '';
9904
+ bc;
9905
+ pollTimer;
9906
+ pollStartedAt = 0;
9907
+ ngOnInit() {
9562
9908
  if (isPlatformBrowser(this.platformId)) {
9563
- this.window = window;
9564
- this.localStorage = localStorage;
9909
+ this.windowRef = window;
9910
+ if ('BroadcastChannel' in window) {
9911
+ this.bc = new BroadcastChannel('mp_payment');
9912
+ this.bc.onmessage = (e) => this.onMpMessage(e?.data);
9913
+ }
9914
+ window.addEventListener('storage', this.onStorage);
9915
+ window.addEventListener('message', this.onWindowMessage);
9565
9916
  }
9566
- }
9567
- ngOnInit() {
9568
9917
  this.getPreference();
9569
9918
  }
9919
+ ngOnDestroy() {
9920
+ if (!isPlatformBrowser(this.platformId))
9921
+ return;
9922
+ this.bc?.close();
9923
+ window.removeEventListener('storage', this.onStorage);
9924
+ window.removeEventListener('message', this.onWindowMessage);
9925
+ if (this.pollTimer)
9926
+ clearInterval(this.pollTimer);
9927
+ }
9928
+ /** Cancela manualmente el pago y finaliza el flujo con estado "cancel". */
9570
9929
  clickClose = () => {
9571
- /* this.closeModal = 'cancel'
9572
- this.ventana?.close() */
9930
+ if (this.finished)
9931
+ return;
9932
+ this.finishWithState('cancel');
9573
9933
  };
9934
+ /**
9935
+ * Inicia el pago abriendo `init_point` en una ventana/pestaña nueva.
9936
+ * Genera un SID y lo persiste para casar la respuesta del catch.
9937
+ * Si el popup es bloqueado, hace fallback navegando en la misma pestaña.
9938
+ */
9574
9939
  iniciar = () => {
9575
- this.closeModal = '';
9576
- this.clearStorageState();
9577
- this.ventana = this.window?.open(this.url);
9578
- this.callState();
9579
- };
9580
- callState = () => {
9581
- let state = this.closeModal != '' ? this.closeModal : this.localStorage?.getItem('state');
9582
- !state && this.ventana.closed && (state = 'cancel');
9583
- this.loading = true;
9584
- state && console.log(state);
9585
- if (state) {
9586
- this.loading = false;
9587
- this.localStorage?.removeItem('state');
9588
- if (state == 'success') {
9589
- this.ventana?.close();
9590
- this.ready.emit(true);
9591
- return;
9592
- }
9593
- if (state == 'pending') {
9594
- this.ventana?.close();
9595
- this.ready.emit(true);
9596
- return;
9597
- }
9598
- if (state == 'failure') {
9599
- this.ventana?.close();
9600
- this.processError('');
9601
- return;
9602
- }
9603
- if (state == 'cancel') {
9604
- this.ventana?.close();
9605
- this.processError('Se cancelo el pago con mercado pago');
9940
+ if (!isPlatformBrowser(this.platformId) || !this.windowRef || !this.url)
9941
+ return;
9942
+ this.phase = 'paying';
9943
+ this.sid = this.genSid();
9944
+ const url = new URL(this.url);
9945
+ localStorage.setItem('mp:sid', this.sid);
9946
+ this.ventana = this.windowRef.open(this.url, '_blank');
9947
+ // popup bloqueado fallback a navegación en misma pestaña
9948
+ if (!this.ventana || this.ventana.closed) {
9949
+ this.windowRef.location.href = this.url;
9950
+ return;
9951
+ }
9952
+ // polling de último recurso (hasta 10 minutos)
9953
+ this.pollStartedAt = Date.now();
9954
+ if (this.pollTimer)
9955
+ clearInterval(this.pollTimer);
9956
+ this.pollTimer = setInterval(() => {
9957
+ if (Date.now() - this.pollStartedAt > 10 * 60 * 1000) {
9958
+ clearInterval(this.pollTimer);
9959
+ this.pollTimer = null;
9960
+ this.phase = 'idle';
9961
+ this.processError('Tiempo de espera agotado al procesar el pago.');
9606
9962
  return;
9607
9963
  }
9608
- this.ventana?.close();
9609
- this.processError('');
9964
+ this.checkLocalStorageOnce();
9965
+ }, 1000);
9966
+ };
9967
+ onWindowMessage = (event) => {
9968
+ const data = event?.data;
9969
+ this.onMpMessage(data);
9970
+ };
9971
+ onStorage = (e) => {
9972
+ if (!e.key || !this.sid)
9610
9973
  return;
9974
+ if (e.key === `mp:state:${this.sid}` && e.newValue) {
9975
+ const state = e.newValue;
9976
+ this.finishWithState(state);
9611
9977
  }
9612
- setTimeout(() => {
9613
- this.callState();
9614
- }, 5000);
9615
9978
  };
9616
- processError = (err) => {
9617
- this._toastService.show(err != '' ? err : 'payment-error');
9618
- // console.log("ERROR ENVIO BACK ", err);
9979
+ onMpMessage = (data) => {
9980
+ if (!data || data.type !== 'mp:state')
9981
+ return;
9982
+ if (data.sid !== this.sid)
9983
+ return;
9984
+ this.finishWithState(data.state);
9619
9985
  };
9986
+ checkLocalStorageOnce() {
9987
+ if (!this.sid)
9988
+ return;
9989
+ const state = localStorage.getItem(`mp:state:${this.sid}`);
9990
+ if (state)
9991
+ this.finishWithState(state);
9992
+ }
9993
+ /** Cierra el flujo de pago con el estado final y notifica al padre. */
9994
+ finishWithState(state) {
9995
+ if (this.finished)
9996
+ return;
9997
+ this.finished = true;
9998
+ if (this.pollTimer)
9999
+ clearInterval(this.pollTimer);
10000
+ this.pollTimer = null;
10001
+ localStorage.removeItem(`mp:state:${this.sid}`);
10002
+ localStorage.removeItem('mp:sid');
10003
+ localStorage.removeItem('state');
10004
+ try {
10005
+ this.ventana && !this.ventana.closed && this.ventana.close();
10006
+ }
10007
+ catch { }
10008
+ this.ventana = null;
10009
+ if (state === 'success' || state === 'pending') {
10010
+ this.phase = 'finalizing';
10011
+ this.ready.emit(true);
10012
+ }
10013
+ else if (state === 'failure' || state === 'cancel') {
10014
+ this.phase = 'idle';
10015
+ this._toastService.show(state === 'cancel' ? 'Se canceló el pago con Mercado Pago' : 'payment-error');
10016
+ }
10017
+ else {
10018
+ this.phase = 'idle';
10019
+ this._toastService.show('payment-error');
10020
+ }
10021
+ }
10022
+ genSid() {
10023
+ return `mp_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
10024
+ }
10025
+ processError = (msg) => {
10026
+ this._toastService.show(msg || 'payment-error');
10027
+ };
10028
+ /** Limpia posibles residuos de estado en storages. */
9620
10029
  clearStorageState = () => {
9621
- sessionStorage.removeItem('state');
9622
- this.localStorage?.removeItem('state');
10030
+ if (!isPlatformBrowser(this.platformId))
10031
+ return;
10032
+ localStorage.removeItem('state');
10033
+ const sid = localStorage.getItem('mp:sid');
10034
+ if (sid)
10035
+ localStorage.removeItem(`mp:state:${sid}`);
10036
+ };
10037
+ /** Obtiene la preferencia e inicializa `url` (init_point). */
10038
+ getPreference = () => {
10039
+ this._paymentService.getPreference(this.allData).then((res) => {
10040
+ this.preference = res;
10041
+ this.url = this.preference?.init_point;
10042
+ this.renderMP(this.preference);
10043
+ }, () => this.setError('operation-error'));
9623
10044
  };
9624
- getPreference = () => this._paymentService.getPreference(this.allData).then(res => {
9625
- this.preference = res;
9626
- this.url = this.preference.init_point;
9627
- this.renderMP(this.preference);
9628
- }, err => this.setError('operation-error'));
9629
10045
  setError = (message) => {
9630
10046
  //this.error = message;
9631
10047
  };
9632
- renderMP = (preference) => {
9633
- this.window?.addEventListener("message", (event) => {
9634
- if (event.origin !== 'https://www.mercadopago.com.ar' || !event.data.type) {
9635
- return;
9636
- }
9637
- let dataType = event.data.type;
9638
- if (dataType === 'submit') {
9639
- const paymentData = event.data.value;
9640
- return;
9641
- }
9642
- });
9643
- };
10048
+ renderMP = (_pref) => { };
9644
10049
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MpRedirectEcComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
9645
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.13", type: MpRedirectEcComponent, isStandalone: true, selector: "app-mp-redirect-ec", inputs: { method: "method", total_amount: "total_amount", allData: "allData" }, outputs: { ready: "ready" }, ngImport: i0, template: "<div class=\"text-center\">\r\n\t@if(url){\r\n\t\t@if(!loading){\r\n\t\t\t<button (click)=\"iniciar()\" class=\"btn btn-outline-primary rounded-0 comprar mt-3\">Pagar</button>\r\n\t\t} @else {\r\n\t\t\t<div class=\"d-flex flex-column jusitfy-content-center align-items-center mt-2\">\r\n\t\t\t\t<h3>Procesando el pago por mercado pago</h3>\r\n\t\t\t\t<h5>Recuerde hacer click en \"Volver al sitio\" desde mercado pago para finalizar la compra.</h5>\r\n\t\t\t\t<app-loading-full-ec></app-loading-full-ec>\r\n\t\t\t</div>\r\n\t\t\t<div class=\"container-fluid\">\r\n\t\t\t\t<div class=\"row\">\r\n\t\t\t\t\t<div class=\"col-5\">\r\n\t\t\t\t\t\t<hr>\r\n\t\t\t\t\t</div>\r\n\t\t\t\t\t<div class=\"col-2 text-center\">\r\n\t\t\t\t\t\t<label for=\"\">o</label>\r\n\t\t\t\t\t</div>\r\n\t\t\t\t\t<div class=\"col-5\">\r\n\t\t\t\t\t\t<hr>\r\n\t\t\t\t\t</div>\r\n\t\t\t\t</div>\r\n\t\t\t</div>\r\n\t\t\t<div class=\"d-flex flex-column justify-content-center align-items-center mt-2\">\r\n\t\t\t\t<button (click)=\"clickClose()\" class=\"btn btn-outline-secondary\">Cancelar pago</button>\r\n\t\t\t</div>\r\n\t\t}\r\n\t} @else{\r\n\t<div class=\"d-flex flex-column justify-content-center align-items-center mt-2\">\r\n\t\t<app-loading-full-ec></app-loading-full-ec>\r\n\t</div>\r\n\t}\r\n</div>", styles: [""], dependencies: [{ kind: "component", type: LoadingFullEcComponent, selector: "app-loading-full-ec" }] });
10050
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.13", type: MpRedirectEcComponent, isStandalone: true, selector: "app-mp-redirect-ec", inputs: { method: "method", total_amount: "total_amount", allData: "allData" }, outputs: { ready: "ready" }, ngImport: i0, template: "<div class=\"text-center\">\r\n\t@if(url) {\r\n\r\n\t@if(isIdle) {\r\n\t<button (click)=\"iniciar()\" class=\"btn btn-outline-primary rounded-0 comprar mt-3\">Pagar</button>\r\n\t}\r\n\r\n\t@if(isPaying) {\r\n\t<div class=\"d-flex flex-column jusitfy-content-center align-items-center mt-2\">\r\n\t\t<h3>Procesando el pago por Mercado Pago</h3>\r\n\t\t<h5>Record\u00E1 tocar \u201CVolver al sitio\u201D en Mercado Pago para finalizar.</h5>\r\n\t\t<app-loading-full-ec></app-loading-full-ec>\r\n\t</div>\r\n\r\n\t<div class=\"container-fluid\">\r\n\t\t<div class=\"row\">\r\n\t\t\t<div class=\"col-5\">\r\n\t\t\t\t<hr>\r\n\t\t\t</div>\r\n\t\t\t<div class=\"col-2 text-center\"><label>o</label></div>\r\n\t\t\t<div class=\"col-5\">\r\n\t\t\t\t<hr>\r\n\t\t\t</div>\r\n\t\t</div>\r\n\t</div>\r\n\r\n\t<div class=\"d-flex flex-column justify-content-center align-items-center mt-2\">\r\n\t\t<button (click)=\"clickClose()\" class=\"btn btn-outline-secondary\">Cancelar pago</button>\r\n\t</div>\r\n\t}\r\n\r\n\t@if(isFinalizing) {\r\n\t<div class=\"d-flex flex-column jusitfy-content-center align-items-center mt-2\">\r\n\t\t<h3>Confirmando pago y redirigiendo\u2026</h3>\r\n\t\t<h5>No cierres ni recargues esta p\u00E1gina.</h5>\r\n\t\t<app-loading-full-ec></app-loading-full-ec>\r\n\t</div>\r\n\t}\r\n\r\n\t} @else {\r\n\t<div class=\"d-flex flex-column justify-content-center align-items-center mt-2\">\r\n\t\t<app-loading-full-ec></app-loading-full-ec>\r\n\t</div>\r\n\t}\r\n</div>", styles: [""], dependencies: [{ kind: "component", type: LoadingFullEcComponent, selector: "app-loading-full-ec" }] });
9646
10051
  }
9647
10052
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MpRedirectEcComponent, decorators: [{
9648
10053
  type: Component,
9649
- args: [{ selector: 'app-mp-redirect-ec', standalone: true, imports: [LoadingFullEcComponent], template: "<div class=\"text-center\">\r\n\t@if(url){\r\n\t\t@if(!loading){\r\n\t\t\t<button (click)=\"iniciar()\" class=\"btn btn-outline-primary rounded-0 comprar mt-3\">Pagar</button>\r\n\t\t} @else {\r\n\t\t\t<div class=\"d-flex flex-column jusitfy-content-center align-items-center mt-2\">\r\n\t\t\t\t<h3>Procesando el pago por mercado pago</h3>\r\n\t\t\t\t<h5>Recuerde hacer click en \"Volver al sitio\" desde mercado pago para finalizar la compra.</h5>\r\n\t\t\t\t<app-loading-full-ec></app-loading-full-ec>\r\n\t\t\t</div>\r\n\t\t\t<div class=\"container-fluid\">\r\n\t\t\t\t<div class=\"row\">\r\n\t\t\t\t\t<div class=\"col-5\">\r\n\t\t\t\t\t\t<hr>\r\n\t\t\t\t\t</div>\r\n\t\t\t\t\t<div class=\"col-2 text-center\">\r\n\t\t\t\t\t\t<label for=\"\">o</label>\r\n\t\t\t\t\t</div>\r\n\t\t\t\t\t<div class=\"col-5\">\r\n\t\t\t\t\t\t<hr>\r\n\t\t\t\t\t</div>\r\n\t\t\t\t</div>\r\n\t\t\t</div>\r\n\t\t\t<div class=\"d-flex flex-column justify-content-center align-items-center mt-2\">\r\n\t\t\t\t<button (click)=\"clickClose()\" class=\"btn btn-outline-secondary\">Cancelar pago</button>\r\n\t\t\t</div>\r\n\t\t}\r\n\t} @else{\r\n\t<div class=\"d-flex flex-column justify-content-center align-items-center mt-2\">\r\n\t\t<app-loading-full-ec></app-loading-full-ec>\r\n\t</div>\r\n\t}\r\n</div>" }]
9650
- }], ctorParameters: () => [], propDecorators: { method: [{
10054
+ args: [{ selector: 'app-mp-redirect-ec', standalone: true, imports: [LoadingFullEcComponent], template: "<div class=\"text-center\">\r\n\t@if(url) {\r\n\r\n\t@if(isIdle) {\r\n\t<button (click)=\"iniciar()\" class=\"btn btn-outline-primary rounded-0 comprar mt-3\">Pagar</button>\r\n\t}\r\n\r\n\t@if(isPaying) {\r\n\t<div class=\"d-flex flex-column jusitfy-content-center align-items-center mt-2\">\r\n\t\t<h3>Procesando el pago por Mercado Pago</h3>\r\n\t\t<h5>Record\u00E1 tocar \u201CVolver al sitio\u201D en Mercado Pago para finalizar.</h5>\r\n\t\t<app-loading-full-ec></app-loading-full-ec>\r\n\t</div>\r\n\r\n\t<div class=\"container-fluid\">\r\n\t\t<div class=\"row\">\r\n\t\t\t<div class=\"col-5\">\r\n\t\t\t\t<hr>\r\n\t\t\t</div>\r\n\t\t\t<div class=\"col-2 text-center\"><label>o</label></div>\r\n\t\t\t<div class=\"col-5\">\r\n\t\t\t\t<hr>\r\n\t\t\t</div>\r\n\t\t</div>\r\n\t</div>\r\n\r\n\t<div class=\"d-flex flex-column justify-content-center align-items-center mt-2\">\r\n\t\t<button (click)=\"clickClose()\" class=\"btn btn-outline-secondary\">Cancelar pago</button>\r\n\t</div>\r\n\t}\r\n\r\n\t@if(isFinalizing) {\r\n\t<div class=\"d-flex flex-column jusitfy-content-center align-items-center mt-2\">\r\n\t\t<h3>Confirmando pago y redirigiendo\u2026</h3>\r\n\t\t<h5>No cierres ni recargues esta p\u00E1gina.</h5>\r\n\t\t<app-loading-full-ec></app-loading-full-ec>\r\n\t</div>\r\n\t}\r\n\r\n\t} @else {\r\n\t<div class=\"d-flex flex-column justify-content-center align-items-center mt-2\">\r\n\t\t<app-loading-full-ec></app-loading-full-ec>\r\n\t</div>\r\n\t}\r\n</div>" }]
10055
+ }], propDecorators: { method: [{
9651
10056
  type: Input
9652
10057
  }], total_amount: [{
9653
10058
  type: Input
@@ -11767,5 +12172,5 @@ const directives = [
11767
12172
  * Generated bundle index. Do not edit.
11768
12173
  */
11769
12174
 
11770
- 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 };
12175
+ 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 };
11771
12176
  //# sourceMappingURL=ng-easycommerce-v18.mjs.map