ng-easycommerce-v18 0.3.0 → 0.3.1-9.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.
- package/README.md +40 -0
- package/assets/ec-i18n/ct.json +1 -0
- package/assets/ec-i18n/en.json +88 -83
- package/assets/ec-i18n/es.json +6 -1
- package/assets/ec-i18n/fr.json +1 -0
- package/assets/ec-i18n/gl.json +1 -0
- package/assets/ec-i18n/pr.json +1 -0
- package/esm2022/lib/classes/component-helper.mjs +11 -2
- package/esm2022/lib/constants/core.constants.service.mjs +107 -2
- package/esm2022/lib/ec-components/abstractions-components/menu-ec.component.mjs +9 -1
- package/esm2022/lib/ec-components/auth-ec/login-form-ec/login-form-ec.component.mjs +5 -1
- package/esm2022/lib/ec-components/auth-ec/register-form-ec/register-form-ec.component.mjs +5 -1
- package/esm2022/lib/ec-components/blocks-ec/block-newsletter-ec/block-newsletter-ec.component.mjs +9 -3
- package/esm2022/lib/ec-components/cart-ec/cart-item-ec/cart-item-ec.component.mjs +42 -10
- package/esm2022/lib/ec-components/checkout-ec/payment-ec/payment-methods/mp-redirect-ec/mp-redirect-ec.component.mjs +141 -75
- package/esm2022/lib/ec-components/filters-ec/filters-ec.component.mjs +9 -1
- package/esm2022/lib/ec-components/header-ec/header-ec.component.mjs +53 -2
- package/esm2022/lib/ec-components/product-detail-ec/product-detail-ec.component.mjs +7 -5
- package/esm2022/lib/ec-components/product-ec/product-ec.component.mjs +143 -5
- package/esm2022/lib/ec-components/widgets-ec/decidir-ec/decidir-ec.component.mjs +3 -3
- package/esm2022/lib/ec-components/widgets-ec/index.mjs +2 -1
- package/esm2022/lib/ec-components/widgets-ec/redsys-catch-ec/redsys-catch-ec.component.mjs +193 -0
- package/esm2022/lib/ec-services/cart.service.mjs +22 -4
- package/esm2022/lib/ec-services/form.service.mjs +13 -1
- package/esm2022/lib/ec-services/order-utility.service.mjs +11 -5
- package/esm2022/lib/ec-services/product-detail.service.mjs +11 -1
- package/esm2022/lib/interfaces/environment.mjs +1 -1
- package/esm2022/lib/interfaces/product.mjs +1 -1
- package/fesm2022/ng-easycommerce-v18.mjs +775 -116
- package/fesm2022/ng-easycommerce-v18.mjs.map +1 -1
- package/lib/classes/component-helper.d.ts +1 -1
- package/lib/constants/core.constants.service.d.ts +24 -0
- package/lib/ec-components/abstractions-components/menu-ec.component.d.ts +6 -0
- package/lib/ec-components/auth-ec/login-form-ec/login-form-ec.component.d.ts +2 -0
- package/lib/ec-components/auth-ec/register-form-ec/register-form-ec.component.d.ts +2 -0
- package/lib/ec-components/cart-ec/cart-item-ec/cart-item-ec.component.d.ts +6 -1
- package/lib/ec-components/checkout-ec/payment-ec/payment-methods/mp-redirect-ec/mp-redirect-ec.component.d.ts +38 -16
- package/lib/ec-components/filters-ec/filters-ec.component.d.ts +6 -0
- package/lib/ec-components/header-ec/header-ec.component.d.ts +16 -2
- package/lib/ec-components/product-detail-ec/product-detail-ec.component.d.ts +1 -0
- package/lib/ec-components/product-ec/product-ec.component.d.ts +14 -1
- package/lib/ec-components/widgets-ec/index.d.ts +1 -0
- package/lib/ec-components/widgets-ec/redsys-catch-ec/redsys-catch-ec.component.d.ts +47 -0
- package/lib/ec-services/cart.service.d.ts +1 -1
- package/lib/ec-services/form.service.d.ts +6 -0
- package/lib/interfaces/environment.d.ts +1 -0
- package/lib/interfaces/product.d.ts +6 -0
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { InjectionToken, makeEnvironmentProviders, inject, Injectable, PLATFORM_ID, RendererFactory2, afterNextRender, signal, EnvironmentInjector, runInInjectionContext, Component, ChangeDetectorRef, HostListener, CUSTOM_ELEMENTS_SCHEMA, Input, Pipe, Injector, EventEmitter, Output, forwardRef, afterRender, ViewChild, computed, Renderer2, ChangeDetectionStrategy, Directive
|
|
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 {
|
|
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
|
|
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';
|
|
@@ -225,6 +225,14 @@ class CoreConstantsService {
|
|
|
225
225
|
* Provee el ID de la plataforma en donde se esta ejecutando el codigo, esto sirve para poder diferenciar si es de un servidor o un navegador web.
|
|
226
226
|
*/
|
|
227
227
|
platformId = inject(PLATFORM_ID);
|
|
228
|
+
/**
|
|
229
|
+
* Document token para acceso SSR-compatible al documento
|
|
230
|
+
*/
|
|
231
|
+
document = inject(DOCUMENT);
|
|
232
|
+
/**
|
|
233
|
+
* Contiene los datos provisto por el frontend en el archivo environment.ts
|
|
234
|
+
*/
|
|
235
|
+
environment = inject(ENVIRONMENT_TOKEN);
|
|
228
236
|
/**
|
|
229
237
|
* Guarda la variable window del web browser.
|
|
230
238
|
*/
|
|
@@ -264,6 +272,102 @@ class CoreConstantsService {
|
|
|
264
272
|
* @returns
|
|
265
273
|
*/
|
|
266
274
|
url = () => this.apiConstants.API_URL.replace(/\/$/, ''); // remove last slash
|
|
275
|
+
/**
|
|
276
|
+
* URL del sitio frontend - Compatible con Angular SSR
|
|
277
|
+
*/
|
|
278
|
+
get FRONTEND_URL() {
|
|
279
|
+
// Verificar si estamos en el navegador
|
|
280
|
+
if (isPlatformBrowser(this.platformId)) {
|
|
281
|
+
// Primero intenta leer de window.__env (configurado por Docker/CI)
|
|
282
|
+
const windowEnv = this.window?.__env;
|
|
283
|
+
if (windowEnv?.frontendUrl) {
|
|
284
|
+
// Log para debugging en desarrollo
|
|
285
|
+
if (!windowEnv.frontendUrl.startsWith('http')) {
|
|
286
|
+
console.warn('[FRONTEND_URL] Invalid frontendUrl format:', windowEnv.frontendUrl);
|
|
287
|
+
}
|
|
288
|
+
return windowEnv.frontendUrl;
|
|
289
|
+
}
|
|
290
|
+
// Si no hay configuración específica, construir desde window.location
|
|
291
|
+
if (this.window?.location) {
|
|
292
|
+
const url = `${this.window.location.protocol}//${this.window.location.host}`;
|
|
293
|
+
// Verificar que no sea localhost o 127.0.0.1 en producción
|
|
294
|
+
if (url.includes('127.0.0.1') || url.includes('localhost')) {
|
|
295
|
+
console.warn('[FRONTEND_URL] Using localhost URL in production, consider setting window.__env.frontendUrl');
|
|
296
|
+
}
|
|
297
|
+
return url;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
// Para SSR, intentar construir desde el document si está disponible
|
|
301
|
+
if (this.document?.location) {
|
|
302
|
+
return `${this.document.location.protocol}//${this.document.location.host}`;
|
|
303
|
+
}
|
|
304
|
+
// Intentar obtener desde environment
|
|
305
|
+
if (this.environment.frontendUrl) {
|
|
306
|
+
return this.environment.frontendUrl;
|
|
307
|
+
}
|
|
308
|
+
// Último fallback - usar la URL del API si está configurada y es absoluta
|
|
309
|
+
const apiUrl = this.apiConstants.API_URL;
|
|
310
|
+
if (apiUrl && (apiUrl.startsWith('http://') || apiUrl.startsWith('https://'))) {
|
|
311
|
+
try {
|
|
312
|
+
const url = new URL(apiUrl);
|
|
313
|
+
return `${url.protocol}//${url.host}`;
|
|
314
|
+
}
|
|
315
|
+
catch (e) {
|
|
316
|
+
console.warn('[FRONTEND_URL] Could not parse API URL as fallback:', apiUrl);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
// Fallback final - retornar URL vacía
|
|
320
|
+
console.warn('[FRONTEND_URL] No valid frontend URL found, this may cause issues with meta tags');
|
|
321
|
+
return '';
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Retorna la URL completa del frontend con una ruta opcional
|
|
325
|
+
* @param path - Ruta opcional para agregar a la URL base
|
|
326
|
+
* @returns {string} URL completa del frontend
|
|
327
|
+
*/
|
|
328
|
+
getFrontendUrl = (path) => {
|
|
329
|
+
const baseUrl = this.FRONTEND_URL;
|
|
330
|
+
if (!path) {
|
|
331
|
+
return baseUrl;
|
|
332
|
+
}
|
|
333
|
+
// Asegurar que la ruta comience con '/' y no tenga doble slash
|
|
334
|
+
const cleanPath = path.startsWith('/') ? path : `/${path}`;
|
|
335
|
+
const cleanBaseUrl = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl;
|
|
336
|
+
return `${cleanBaseUrl}${cleanPath}`;
|
|
337
|
+
};
|
|
338
|
+
/**
|
|
339
|
+
* Retorna la URL absoluta de una imagen para meta tags (compatible con SSR)
|
|
340
|
+
* @param postMedia - Ruta de la imagen
|
|
341
|
+
* @returns {string} URL absoluta de la imagen
|
|
342
|
+
*/
|
|
343
|
+
getAbsoluteImageUrl = (postMedia) => {
|
|
344
|
+
// Si la URL ya es absoluta, devolverla tal como está
|
|
345
|
+
if (postMedia && (postMedia.startsWith('http://') || postMedia.startsWith('https://'))) {
|
|
346
|
+
return postMedia;
|
|
347
|
+
}
|
|
348
|
+
// Construir la URL de la imagen usando la URL del API
|
|
349
|
+
const imageUrl = this.mediaImageUrl(postMedia);
|
|
350
|
+
// Si la URL de la imagen ya es absoluta, devolverla
|
|
351
|
+
if (imageUrl.startsWith('http://') || imageUrl.startsWith('https://')) {
|
|
352
|
+
return imageUrl;
|
|
353
|
+
}
|
|
354
|
+
// Si es una URL relativa, convertirla a absoluta usando la URL base del API
|
|
355
|
+
const apiBaseUrl = this.apiConstants.API_URL;
|
|
356
|
+
if (apiBaseUrl.startsWith('http://') || apiBaseUrl.startsWith('https://')) {
|
|
357
|
+
// API_URL ya es absoluta
|
|
358
|
+
return imageUrl;
|
|
359
|
+
}
|
|
360
|
+
// Si API_URL es relativa, usar la URL del frontend como base
|
|
361
|
+
const frontendUrl = this.FRONTEND_URL;
|
|
362
|
+
if (!frontendUrl) {
|
|
363
|
+
return imageUrl; // Fallback si no podemos determinar la URL del frontend
|
|
364
|
+
}
|
|
365
|
+
// Construir URL absoluta
|
|
366
|
+
const cleanApiUrl = apiBaseUrl.startsWith('/') ? apiBaseUrl.substring(1) : apiBaseUrl;
|
|
367
|
+
const cleanFrontendUrl = frontendUrl.endsWith('/') ? frontendUrl.slice(0, -1) : frontendUrl;
|
|
368
|
+
const cleanImageUrl = imageUrl.startsWith('/') ? imageUrl.substring(1) : imageUrl;
|
|
369
|
+
return `${cleanFrontendUrl}/${cleanApiUrl}${cleanImageUrl}`;
|
|
370
|
+
};
|
|
267
371
|
/**
|
|
268
372
|
* Retorna la url de las imagenes de los banners
|
|
269
373
|
* @returns
|
|
@@ -2426,6 +2530,12 @@ class FormService {
|
|
|
2426
2530
|
sendResponse(response_obj) {
|
|
2427
2531
|
return this.connection.post(this.contactFormResponseAPI(), this.prepareData(response_obj));
|
|
2428
2532
|
}
|
|
2533
|
+
/**
|
|
2534
|
+
* @description contiene un arreglo de string cuyo contenido son los codigos de los paises a excluir.
|
|
2535
|
+
* @example ['GB']
|
|
2536
|
+
*/
|
|
2537
|
+
excludedCountries = [];
|
|
2538
|
+
allowedCountries = [];
|
|
2429
2539
|
/**
|
|
2430
2540
|
* Obtiene los paises y los guarda en un observable.
|
|
2431
2541
|
* @returns
|
|
@@ -2441,6 +2551,12 @@ class FormService {
|
|
|
2441
2551
|
const documentTypesData = documentTypesLocale['documentTypes'];
|
|
2442
2552
|
this.documentTypesSubject.next(documentTypesData);
|
|
2443
2553
|
})).subscribe();
|
|
2554
|
+
if (this.excludedCountries.length > 0) {
|
|
2555
|
+
this.countries$ = this.countries$.pipe(map(countries => countries.filter(country => !this.excludedCountries.includes(country.code))));
|
|
2556
|
+
}
|
|
2557
|
+
if (this.allowedCountries.length > 0) {
|
|
2558
|
+
this.countries$ = this.countries$.pipe(map(countries => countries.filter(country => this.allowedCountries.includes(country.code))));
|
|
2559
|
+
}
|
|
2444
2560
|
return this.countries$;
|
|
2445
2561
|
}
|
|
2446
2562
|
/**
|
|
@@ -4377,8 +4493,15 @@ class CartService {
|
|
|
4377
4493
|
* @param quantity la cantidad nueva.
|
|
4378
4494
|
*/
|
|
4379
4495
|
updateItemQuantity(item, quantity) {
|
|
4496
|
+
// Validar antes de proceder
|
|
4380
4497
|
if (this.validateQuantity(item, quantity) && (this.validatePriceAndCredits(item, quantity))) {
|
|
4381
|
-
|
|
4498
|
+
// Si pasa las validaciones, hacer la petición HTTP
|
|
4499
|
+
firstValueFrom(this._connection.put(this.updateItemQuantityApi(this.findItemByIdVariant(item.variant_id)), { 'quantity': Number(quantity) }))
|
|
4500
|
+
.then(res => this.updateCartObj(res) && this.updateCartItemQuantity(item.variant_id, Number(quantity)), err => this.handleError(err));
|
|
4501
|
+
return true; // Validaciones pasaron, operación iniciada
|
|
4502
|
+
}
|
|
4503
|
+
else {
|
|
4504
|
+
return false; // Validaciones fallaron
|
|
4382
4505
|
}
|
|
4383
4506
|
}
|
|
4384
4507
|
/**
|
|
@@ -4459,14 +4582,25 @@ class CartService {
|
|
|
4459
4582
|
* @returns
|
|
4460
4583
|
*/
|
|
4461
4584
|
validateQuantity(item, quantity) {
|
|
4462
|
-
|
|
4585
|
+
const actualQuantity = Number(this.getCountFromItemInCart(item.productCode)); // <-- Forzar a número
|
|
4586
|
+
const newQuantity = Number(quantity); // <-- Forzar a número
|
|
4587
|
+
if ((actualQuantity + newQuantity) > item.product.variants[0].maximumItemsQuantity) {
|
|
4463
4588
|
this._toastService.show('maximum-items-quantity', { quantity: item.product.variants[0].maximumItemsQuantity });
|
|
4464
4589
|
return false;
|
|
4465
4590
|
}
|
|
4466
|
-
if ((
|
|
4591
|
+
if ((actualQuantity + newQuantity) < item.product.variants[0].minimumItemsQuantity) {
|
|
4467
4592
|
this._toastService.show('minimum-items-quantity', { quantity: item.product.variants[0].minimumItemsQuantity });
|
|
4468
4593
|
return false;
|
|
4469
4594
|
}
|
|
4595
|
+
if (item.product.variants[0].multipleQuantity && item.product.variants[0].multipleQuantity > 1) {
|
|
4596
|
+
if ((actualQuantity + newQuantity) % item.product.variants[0].multipleQuantity !== 0) {
|
|
4597
|
+
this._toastService.show('multiple-quantity-required', {
|
|
4598
|
+
multiple: item.product.variants[0].multipleQuantity,
|
|
4599
|
+
current: actualQuantity + newQuantity // <-- Ahora suma correctamente
|
|
4600
|
+
});
|
|
4601
|
+
return false;
|
|
4602
|
+
}
|
|
4603
|
+
}
|
|
4470
4604
|
return true;
|
|
4471
4605
|
}
|
|
4472
4606
|
/**
|
|
@@ -4814,6 +4948,7 @@ class ProductDetailService {
|
|
|
4814
4948
|
associated.minimumItemsQuantity = v.stock > 0 && 'minimumItemsQuantity' in v && v.minimumItemsQuantity > 0
|
|
4815
4949
|
? v.minimumItemsQuantity
|
|
4816
4950
|
: (v.multipleQuantity ?? 1);
|
|
4951
|
+
associated.multipleQuantity = v.multipleQuantity ?? 1;
|
|
4817
4952
|
// Precios al consumidor final
|
|
4818
4953
|
associated.finalConsumer = {
|
|
4819
4954
|
finalConsumerPrice: v.final_consumer_price,
|
|
@@ -5016,6 +5151,15 @@ class ProductDetailService {
|
|
|
5016
5151
|
this._toastService.show('minimum-items-quantity', { quantity: asociatedData.minimumItemsQuantity });
|
|
5017
5152
|
return false;
|
|
5018
5153
|
}
|
|
5154
|
+
if (asociatedData.multipleQuantity && asociatedData.multipleQuantity > 1) {
|
|
5155
|
+
if ((actualQuantity + quantity) % asociatedData.multipleQuantity !== 0) {
|
|
5156
|
+
this._toastService.show('multiple-quantity-required', {
|
|
5157
|
+
multiple: asociatedData.multipleQuantity,
|
|
5158
|
+
current: actualQuantity + quantity
|
|
5159
|
+
});
|
|
5160
|
+
return false;
|
|
5161
|
+
}
|
|
5162
|
+
}
|
|
5019
5163
|
return true;
|
|
5020
5164
|
};
|
|
5021
5165
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ProductDetailService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
@@ -5042,12 +5186,18 @@ class OrderUtilityService {
|
|
|
5042
5186
|
let subtotal = { type: 'subtotal', name: 'subtotal', amount: cart.totals.subtotal };
|
|
5043
5187
|
let total = { type: 'total', name: 'total', amount: cart.totals.total };
|
|
5044
5188
|
!forCart && new_state.push(subtotal);
|
|
5189
|
+
0;
|
|
5045
5190
|
for (let index = 0; index < cart.cartDiscounts.length; index++) {
|
|
5046
|
-
|
|
5191
|
+
const disc = cart.cartDiscounts[index];
|
|
5192
|
+
disc.type = this.formatType(disc.type);
|
|
5193
|
+
let amount = this.getAmount(disc, cart);
|
|
5194
|
+
if (disc.type === 'discount') {
|
|
5195
|
+
amount = cart.totals.promotion;
|
|
5196
|
+
}
|
|
5047
5197
|
new_state.push({
|
|
5048
|
-
type:
|
|
5049
|
-
name: (
|
|
5050
|
-
amount:
|
|
5198
|
+
type: disc.type,
|
|
5199
|
+
name: (disc.type != 'coupon') ? disc.name : 'coupon',
|
|
5200
|
+
amount: amount
|
|
5051
5201
|
});
|
|
5052
5202
|
}
|
|
5053
5203
|
!forCart && new_state.push(total);
|
|
@@ -5884,6 +6034,14 @@ class MenuEcComponent {
|
|
|
5884
6034
|
});
|
|
5885
6035
|
}));
|
|
5886
6036
|
}
|
|
6037
|
+
/**
|
|
6038
|
+
* Verifica si una categoría tiene la propiedad isVisible y está marcada como visible
|
|
6039
|
+
* @param category - La categoría a verificar
|
|
6040
|
+
* @returns true si la categoría es visible, false en caso contrario
|
|
6041
|
+
*/
|
|
6042
|
+
hasVisibleProperty(category) {
|
|
6043
|
+
return category.isVisible === true;
|
|
6044
|
+
}
|
|
5887
6045
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MenuEcComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
5888
6046
|
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 });
|
|
5889
6047
|
}
|
|
@@ -6030,6 +6188,13 @@ class HeaderEcComponent extends MenuEcComponent {
|
|
|
6030
6188
|
hidePrices = false;
|
|
6031
6189
|
__authService = inject(AuthService);
|
|
6032
6190
|
_channelService = inject(ChannelService);
|
|
6191
|
+
changeDetector = inject(ChangeDetectorRef);
|
|
6192
|
+
appRouter = inject(Router);
|
|
6193
|
+
platformId = inject(PLATFORM_ID);
|
|
6194
|
+
mobileDropdownOpen = signal(false);
|
|
6195
|
+
isMenuOpen = signal(false);
|
|
6196
|
+
// Observable del estado de autenticación
|
|
6197
|
+
logged$;
|
|
6033
6198
|
isAuthenticated$ = this.__authService.isAuthenticated();
|
|
6034
6199
|
constructor() {
|
|
6035
6200
|
super();
|
|
@@ -6047,6 +6212,22 @@ class HeaderEcComponent extends MenuEcComponent {
|
|
|
6047
6212
|
this.channel = this.coreConstantsService.getChannel();
|
|
6048
6213
|
this.onWindowScroll();
|
|
6049
6214
|
this.detectRouteChange(); // Llamamos a la función que detecta el cambio de ruta
|
|
6215
|
+
// Usar el Observable del AuthService
|
|
6216
|
+
this.logged$ = this.__authService.loggedIn$;
|
|
6217
|
+
// Suscribirse al Observable y forzar detección de cambios cuando sea necesario
|
|
6218
|
+
this.logged$.subscribe(isLoggedIn => {
|
|
6219
|
+
this.changeDetector.detectChanges();
|
|
6220
|
+
});
|
|
6221
|
+
if (isPlatformBrowser(this.platformId)) {
|
|
6222
|
+
this.appRouter.events.subscribe(evt => {
|
|
6223
|
+
if (evt instanceof NavigationEnd) {
|
|
6224
|
+
// Forzar detección de cambios después de navegación
|
|
6225
|
+
setTimeout(() => {
|
|
6226
|
+
this.changeDetector.detectChanges();
|
|
6227
|
+
}, 100);
|
|
6228
|
+
}
|
|
6229
|
+
});
|
|
6230
|
+
}
|
|
6050
6231
|
}
|
|
6051
6232
|
ngAfterViewInit() {
|
|
6052
6233
|
this.setupMobileMenu(); // Inicializamos el menú móvil
|
|
@@ -6183,6 +6364,33 @@ class HeaderEcComponent extends MenuEcComponent {
|
|
|
6183
6364
|
});
|
|
6184
6365
|
});
|
|
6185
6366
|
}
|
|
6367
|
+
togglePanel(id) {
|
|
6368
|
+
// Solo ejecutar en el navegador para evitar problemas con SSR
|
|
6369
|
+
if (isPlatformBrowser(this.platformId)) {
|
|
6370
|
+
this.openPanels.update(panels => ({
|
|
6371
|
+
...panels,
|
|
6372
|
+
[id]: !panels[id]
|
|
6373
|
+
}));
|
|
6374
|
+
}
|
|
6375
|
+
}
|
|
6376
|
+
openPanels = signal({
|
|
6377
|
+
collapseUno: false,
|
|
6378
|
+
collapseDos: true // Productos abierto por defecto
|
|
6379
|
+
});
|
|
6380
|
+
collapseAllMenus() {
|
|
6381
|
+
// Solo ejecutar en el navegador para evitar problemas con SSR
|
|
6382
|
+
if (isPlatformBrowser(this.platformId)) {
|
|
6383
|
+
this.openPanels.update(panels => {
|
|
6384
|
+
const newPanels = { ...panels };
|
|
6385
|
+
Object.keys(newPanels).forEach(key => {
|
|
6386
|
+
if (key !== 'collapseDos') {
|
|
6387
|
+
newPanels[key] = false;
|
|
6388
|
+
}
|
|
6389
|
+
});
|
|
6390
|
+
return newPanels;
|
|
6391
|
+
});
|
|
6392
|
+
}
|
|
6393
|
+
}
|
|
6186
6394
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: HeaderEcComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
6187
6395
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: HeaderEcComponent, isStandalone: true, selector: "lib-header-ec", host: { listeners: { "window:scroll": "onWindowScroll()" } }, usesInheritance: true, ngImport: i0, template: "<p>header-ec works!</p>\r\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }] });
|
|
6188
6396
|
}
|
|
@@ -6750,6 +6958,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
|
|
|
6750
6958
|
class ProductEcComponent {
|
|
6751
6959
|
injector = inject(Injector);
|
|
6752
6960
|
routerService = inject(Router);
|
|
6961
|
+
_cartService = inject(CartService);
|
|
6962
|
+
_toastService = inject(ToastService);
|
|
6963
|
+
isAddingToCart = signal(false);
|
|
6964
|
+
quantity = signal(1);
|
|
6753
6965
|
/**
|
|
6754
6966
|
* Navega al detalle del producto y sube al inicio de la página
|
|
6755
6967
|
* @param productId ID del producto
|
|
@@ -6822,12 +7034,145 @@ class ProductEcComponent {
|
|
|
6822
7034
|
const discount = ((originalPrice - salePrice) / originalPrice) * 100;
|
|
6823
7035
|
return Math.round(discount);
|
|
6824
7036
|
}
|
|
7037
|
+
plus(stock, multipleQuantity) {
|
|
7038
|
+
const current = Number(this.quantity()); // <-- fuerza a número
|
|
7039
|
+
if (multipleQuantity && multipleQuantity > 0) {
|
|
7040
|
+
stock
|
|
7041
|
+
? (current < stock
|
|
7042
|
+
? this.quantity.set(current + multipleQuantity)
|
|
7043
|
+
: this._toastService.show('out-of-stock-actually'))
|
|
7044
|
+
: this.quantity.set(current + multipleQuantity);
|
|
7045
|
+
}
|
|
7046
|
+
else {
|
|
7047
|
+
stock
|
|
7048
|
+
? (current < stock
|
|
7049
|
+
? this.quantity.set(current + 1)
|
|
7050
|
+
: this._toastService.show('out-of-stock-actually'))
|
|
7051
|
+
: this.quantity.set(current + 1);
|
|
7052
|
+
}
|
|
7053
|
+
}
|
|
7054
|
+
less(multipleQuantity) {
|
|
7055
|
+
const current = Number(this.quantity()); // <-- fuerza a número
|
|
7056
|
+
if (multipleQuantity && multipleQuantity > 0) {
|
|
7057
|
+
current > multipleQuantity
|
|
7058
|
+
? this.quantity.set(current - multipleQuantity)
|
|
7059
|
+
: null;
|
|
7060
|
+
}
|
|
7061
|
+
else {
|
|
7062
|
+
current > 1 ? this.quantity.set(current - 1) : null;
|
|
7063
|
+
}
|
|
7064
|
+
}
|
|
7065
|
+
addToCart() {
|
|
7066
|
+
if (this.isAddingToCart() || this.quantity() <= 0)
|
|
7067
|
+
return;
|
|
7068
|
+
// Verificar que tenemos el producto y sus variantes
|
|
7069
|
+
if (!this.product || !this.product.variants || this.product.variants.length === 0) {
|
|
7070
|
+
this._toastService.show('cant-buy');
|
|
7071
|
+
return;
|
|
7072
|
+
}
|
|
7073
|
+
// Verificar stock
|
|
7074
|
+
const firstVariant = this.product.variants[0];
|
|
7075
|
+
if (!firstVariant.stock || firstVariant.stock <= 0) {
|
|
7076
|
+
this._toastService.show('out-of-stock');
|
|
7077
|
+
return;
|
|
7078
|
+
}
|
|
7079
|
+
// Verificar que no se supere el stock disponible
|
|
7080
|
+
if (this.quantity() > firstVariant.stock) {
|
|
7081
|
+
this._toastService.show('out-of-stock-actually');
|
|
7082
|
+
return;
|
|
7083
|
+
}
|
|
7084
|
+
// NUEVAS VALIDACIONES: Verificar restricciones del producto
|
|
7085
|
+
if (!this.validateQuantity(this.quantity())) {
|
|
7086
|
+
return; // La validación ya mostró el toast correspondiente
|
|
7087
|
+
}
|
|
7088
|
+
this.isAddingToCart.set(true);
|
|
7089
|
+
try {
|
|
7090
|
+
// Agregar al carrito usando CartService directamente
|
|
7091
|
+
this._cartService.addToCart(this.product, this.quantity(), firstVariant.code);
|
|
7092
|
+
// Resetear cantidad a 1 después de agregar al carrito
|
|
7093
|
+
this.quantity.set(1);
|
|
7094
|
+
}
|
|
7095
|
+
catch (error) {
|
|
7096
|
+
this._toastService.show('cant-buy');
|
|
7097
|
+
}
|
|
7098
|
+
setTimeout(() => {
|
|
7099
|
+
this.isAddingToCart.set(false);
|
|
7100
|
+
}, 2000);
|
|
7101
|
+
}
|
|
7102
|
+
/**
|
|
7103
|
+
* Valida las restricciones de cantidad del producto
|
|
7104
|
+
*/
|
|
7105
|
+
validateQuantity(quantity) {
|
|
7106
|
+
if (!this.product.variants || this.product.variants.length === 0) {
|
|
7107
|
+
this._toastService.show('cant-buy');
|
|
7108
|
+
return false;
|
|
7109
|
+
}
|
|
7110
|
+
const variant = this.product.variants[0];
|
|
7111
|
+
// Obtener cantidad actual en el carrito
|
|
7112
|
+
const actualQuantity = this._cartService.getCountFromItemInCart(variant.code);
|
|
7113
|
+
const totalQuantity = Number(actualQuantity) + Number(quantity);
|
|
7114
|
+
// Obtener restricciones del producto/variante (con valores por defecto)
|
|
7115
|
+
const maximumItemsQuantity = this.product.maximumItemsQuantity ||
|
|
7116
|
+
variant.maximumItemsQuantity ||
|
|
7117
|
+
999999;
|
|
7118
|
+
const minimumItemsQuantity = this.product.minimumItemsQuantity ||
|
|
7119
|
+
variant.minimumItemsQuantity ||
|
|
7120
|
+
1;
|
|
7121
|
+
const multipleQuantity = this.product.multipleQuantity ||
|
|
7122
|
+
variant.multipleQuantity ||
|
|
7123
|
+
1;
|
|
7124
|
+
// Validar cantidad máxima
|
|
7125
|
+
if (totalQuantity > maximumItemsQuantity) {
|
|
7126
|
+
this._toastService.show('maximum-items-quantity', { quantity: maximumItemsQuantity });
|
|
7127
|
+
return false;
|
|
7128
|
+
}
|
|
7129
|
+
// Validar cantidad mínima
|
|
7130
|
+
if (totalQuantity < minimumItemsQuantity) {
|
|
7131
|
+
this._toastService.show('minimum-items-quantity', { quantity: minimumItemsQuantity });
|
|
7132
|
+
return false;
|
|
7133
|
+
}
|
|
7134
|
+
// Validar múltiplo de cantidad
|
|
7135
|
+
if (multipleQuantity && multipleQuantity > 1) {
|
|
7136
|
+
if (totalQuantity % multipleQuantity !== 0) {
|
|
7137
|
+
this._toastService.show('multiple-quantity-required', {
|
|
7138
|
+
multiple: multipleQuantity,
|
|
7139
|
+
current: totalQuantity
|
|
7140
|
+
});
|
|
7141
|
+
return false;
|
|
7142
|
+
}
|
|
7143
|
+
}
|
|
7144
|
+
// Validar que no exceda el stock total
|
|
7145
|
+
if (totalQuantity > variant.stock) {
|
|
7146
|
+
this._toastService.show('out-of-stock-actually');
|
|
7147
|
+
return false;
|
|
7148
|
+
}
|
|
7149
|
+
return true; // Todas las validaciones pasaron
|
|
7150
|
+
}
|
|
7151
|
+
checkStock(stock) {
|
|
7152
|
+
if (this.quantity() >= stock)
|
|
7153
|
+
this.quantity.set(stock);
|
|
7154
|
+
}
|
|
7155
|
+
onQuantityChange(event) {
|
|
7156
|
+
const target = event.target;
|
|
7157
|
+
const value = +target.value;
|
|
7158
|
+
// Validar que el valor sea mayor a 0
|
|
7159
|
+
if (value > 0) {
|
|
7160
|
+
this.quantity.set(value);
|
|
7161
|
+
// Validar contra el stock disponible
|
|
7162
|
+
const maxStock = this.product?.variants?.[0]?.stock || this.product?.stock || 0;
|
|
7163
|
+
this.checkStock(maxStock);
|
|
7164
|
+
}
|
|
7165
|
+
else {
|
|
7166
|
+
// Si el valor es 0 o negativo, resetear a 1
|
|
7167
|
+
this.quantity.set(1);
|
|
7168
|
+
}
|
|
7169
|
+
}
|
|
6825
7170
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ProductEcComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
6826
|
-
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" }] });
|
|
7171
|
+
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 }] });
|
|
6827
7172
|
}
|
|
6828
7173
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ProductEcComponent, decorators: [{
|
|
6829
7174
|
type: Component,
|
|
6830
|
-
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>" }]
|
|
7175
|
+
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>" }]
|
|
6831
7176
|
}], ctorParameters: () => [], propDecorators: { product: [{
|
|
6832
7177
|
type: Input,
|
|
6833
7178
|
args: [{
|
|
@@ -7095,8 +7440,14 @@ class BlockNewsletterEcComponent {
|
|
|
7095
7440
|
this._toastService.show(this.success_message || success_message);
|
|
7096
7441
|
this.loading = false;
|
|
7097
7442
|
}, (err) => {
|
|
7098
|
-
|
|
7099
|
-
|
|
7443
|
+
if (err.status === 400) {
|
|
7444
|
+
this._toastService.error('newsletter_email_already_registered');
|
|
7445
|
+
this.loading = false;
|
|
7446
|
+
}
|
|
7447
|
+
else {
|
|
7448
|
+
this._toastService.error('inquiry-error');
|
|
7449
|
+
this.loading = false;
|
|
7450
|
+
}
|
|
7100
7451
|
});
|
|
7101
7452
|
}
|
|
7102
7453
|
else {
|
|
@@ -7401,6 +7752,212 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
|
|
|
7401
7752
|
args: ['mainCanvas', { static: true }]
|
|
7402
7753
|
}] } });
|
|
7403
7754
|
|
|
7755
|
+
class ComponentHelper {
|
|
7756
|
+
constructor() {
|
|
7757
|
+
this.ecOnConstruct();
|
|
7758
|
+
}
|
|
7759
|
+
ecOnInit = (params = {}) => {
|
|
7760
|
+
};
|
|
7761
|
+
ecOnConstruct = (params = {}) => {
|
|
7762
|
+
};
|
|
7763
|
+
hasParams = (params, searched) => {
|
|
7764
|
+
if (!params || !searched)
|
|
7765
|
+
return false;
|
|
7766
|
+
const q = searched.trim().toLowerCase();
|
|
7767
|
+
return params.some(p => {
|
|
7768
|
+
const code = p?.['code']?.toString().toLowerCase() ?? '';
|
|
7769
|
+
// Primero chequeo exacto, y si no, parcial
|
|
7770
|
+
return code === q || code.includes(q);
|
|
7771
|
+
});
|
|
7772
|
+
};
|
|
7773
|
+
navigateOnRouter(router, url) {
|
|
7774
|
+
router.navigateByUrl(`/${url}`);
|
|
7775
|
+
}
|
|
7776
|
+
}
|
|
7777
|
+
|
|
7778
|
+
/**
|
|
7779
|
+
* Catch genérico para redirecciones de pagos.
|
|
7780
|
+
* - Normaliza el estado recibido por params/query.
|
|
7781
|
+
* - Informa el resultado al opener (postMessage), BroadcastChannel y localStorage.
|
|
7782
|
+
* - Intenta cerrarse; si el cierre falla, redirige según el estado.
|
|
7783
|
+
*/
|
|
7784
|
+
class RedsysCatchEcComponent extends ComponentHelper {
|
|
7785
|
+
activedRoute;
|
|
7786
|
+
router;
|
|
7787
|
+
checkoutService;
|
|
7788
|
+
renderer;
|
|
7789
|
+
elementRef;
|
|
7790
|
+
document;
|
|
7791
|
+
platformId;
|
|
7792
|
+
message = '';
|
|
7793
|
+
subscription = null;
|
|
7794
|
+
sid = '';
|
|
7795
|
+
bc;
|
|
7796
|
+
constructor(activedRoute, router, checkoutService, renderer, elementRef, document, platformId) {
|
|
7797
|
+
super();
|
|
7798
|
+
this.activedRoute = activedRoute;
|
|
7799
|
+
this.router = router;
|
|
7800
|
+
this.checkoutService = checkoutService;
|
|
7801
|
+
this.renderer = renderer;
|
|
7802
|
+
this.elementRef = elementRef;
|
|
7803
|
+
this.document = document;
|
|
7804
|
+
this.platformId = platformId;
|
|
7805
|
+
this.hideHeaderFooter();
|
|
7806
|
+
this.ecOnConstruct();
|
|
7807
|
+
}
|
|
7808
|
+
ngOnInit() {
|
|
7809
|
+
if (isPlatformBrowser(this.platformId) && 'BroadcastChannel' in window) {
|
|
7810
|
+
this.bc = new BroadcastChannel('mp_payment');
|
|
7811
|
+
}
|
|
7812
|
+
this.subscription = combineLatest([this.activedRoute.params, this.activedRoute.queryParams])
|
|
7813
|
+
.subscribe(([routeParams, q]) => {
|
|
7814
|
+
let stateStr = routeParams['state'];
|
|
7815
|
+
if (stateStr === 'statuspayment')
|
|
7816
|
+
stateStr = q['status'];
|
|
7817
|
+
const statusParam = (stateStr || q['status'] || q['state'] || '').toString();
|
|
7818
|
+
const state = this.normalizeState(statusParam);
|
|
7819
|
+
this.sid = (q['sid'] || (isPlatformBrowser(this.platformId) ? localStorage.getItem('mp:sid') : '') || '').toString();
|
|
7820
|
+
this.storeTotalAmount(q);
|
|
7821
|
+
this.setStateInLocal('Su pago fue procesado con éxito.', state);
|
|
7822
|
+
this.signalState(state);
|
|
7823
|
+
this.tryCloseSelf(() => {
|
|
7824
|
+
const target = (state === 'success' || state === 'pending')
|
|
7825
|
+
? ['/checkout/order_success']
|
|
7826
|
+
: ['/checkout'];
|
|
7827
|
+
setTimeout(() => this.router.navigate(target), 4500);
|
|
7828
|
+
});
|
|
7829
|
+
});
|
|
7830
|
+
this.ecOnInit();
|
|
7831
|
+
}
|
|
7832
|
+
ngOnDestroy() {
|
|
7833
|
+
this.subscription?.unsubscribe();
|
|
7834
|
+
this.bc?.close();
|
|
7835
|
+
this.showHeaderFooter();
|
|
7836
|
+
}
|
|
7837
|
+
/** Guarda total_amount si viene desde el PSP. */
|
|
7838
|
+
storeTotalAmount(queryParams) {
|
|
7839
|
+
const totalAmount = queryParams['total_amount'];
|
|
7840
|
+
if (totalAmount && isPlatformBrowser(this.platformId)) {
|
|
7841
|
+
localStorage.setItem('total_amount', totalAmount);
|
|
7842
|
+
}
|
|
7843
|
+
}
|
|
7844
|
+
/** Setea mensaje y estado en storages. */
|
|
7845
|
+
setStateInLocal = (mensaje, state) => {
|
|
7846
|
+
this.message = mensaje;
|
|
7847
|
+
if (!isPlatformBrowser(this.platformId))
|
|
7848
|
+
return;
|
|
7849
|
+
try {
|
|
7850
|
+
localStorage.setItem('state', state);
|
|
7851
|
+
}
|
|
7852
|
+
catch { }
|
|
7853
|
+
try {
|
|
7854
|
+
sessionStorage.setItem('modalnews', 'false');
|
|
7855
|
+
}
|
|
7856
|
+
catch { }
|
|
7857
|
+
};
|
|
7858
|
+
/** Normaliza estados heterogéneos de distintos gateways. */
|
|
7859
|
+
normalizeState(raw) {
|
|
7860
|
+
const v = (raw || '').toLowerCase();
|
|
7861
|
+
if (v === '200' || v === 'ok' || v === 'success')
|
|
7862
|
+
return 'success';
|
|
7863
|
+
if (v === 'pending')
|
|
7864
|
+
return 'pending';
|
|
7865
|
+
if (v === 'cancel')
|
|
7866
|
+
return 'cancel';
|
|
7867
|
+
return 'failure'; // failure, 0, error, rejected, chargeback, desconocidos
|
|
7868
|
+
}
|
|
7869
|
+
/**
|
|
7870
|
+
* Informa el resultado: postMessage al opener, BroadcastChannel y localStorage
|
|
7871
|
+
* (este último permite polling en la pestaña madre).
|
|
7872
|
+
*/
|
|
7873
|
+
signalState(state) {
|
|
7874
|
+
if (!isPlatformBrowser(this.platformId))
|
|
7875
|
+
return;
|
|
7876
|
+
const sid = this.sid || localStorage.getItem('mp:sid') || '';
|
|
7877
|
+
const payload = { type: 'mp:state', sid, state };
|
|
7878
|
+
try {
|
|
7879
|
+
window.opener && window.opener.postMessage(payload, '*');
|
|
7880
|
+
}
|
|
7881
|
+
catch { }
|
|
7882
|
+
try {
|
|
7883
|
+
this.bc?.postMessage(payload);
|
|
7884
|
+
}
|
|
7885
|
+
catch { }
|
|
7886
|
+
try {
|
|
7887
|
+
localStorage.setItem(`mp:state:${sid}`, state);
|
|
7888
|
+
}
|
|
7889
|
+
catch { }
|
|
7890
|
+
try {
|
|
7891
|
+
localStorage.setItem('state', state);
|
|
7892
|
+
}
|
|
7893
|
+
catch { }
|
|
7894
|
+
}
|
|
7895
|
+
/** Intenta cerrar la pestaña actual; si falla, ejecuta el callback de fallback. */
|
|
7896
|
+
tryCloseSelf(onFail) {
|
|
7897
|
+
if (!isPlatformBrowser(this.platformId))
|
|
7898
|
+
return onFail();
|
|
7899
|
+
let attempted = false;
|
|
7900
|
+
try {
|
|
7901
|
+
window.close();
|
|
7902
|
+
attempted = true;
|
|
7903
|
+
}
|
|
7904
|
+
catch { }
|
|
7905
|
+
if (!attempted)
|
|
7906
|
+
onFail();
|
|
7907
|
+
}
|
|
7908
|
+
/** Oculta header/footer para esta pantalla mínima. */
|
|
7909
|
+
hideHeaderFooter() {
|
|
7910
|
+
if (!isPlatformBrowser(this.platformId))
|
|
7911
|
+
return;
|
|
7912
|
+
const header = this.document.querySelector('header');
|
|
7913
|
+
const footer = this.document.querySelector('footer');
|
|
7914
|
+
if (header)
|
|
7915
|
+
this.renderer.setStyle(header, 'display', 'none');
|
|
7916
|
+
if (footer)
|
|
7917
|
+
this.renderer.setStyle(footer, 'display', 'none');
|
|
7918
|
+
}
|
|
7919
|
+
/** Restaura header/footer al salir. */
|
|
7920
|
+
showHeaderFooter() {
|
|
7921
|
+
if (!isPlatformBrowser(this.platformId))
|
|
7922
|
+
return;
|
|
7923
|
+
const header = this.document.querySelector('header');
|
|
7924
|
+
const footer = this.document.querySelector('footer');
|
|
7925
|
+
if (header)
|
|
7926
|
+
this.renderer.removeStyle(header, 'display');
|
|
7927
|
+
if (footer)
|
|
7928
|
+
this.renderer.removeStyle(footer, 'display');
|
|
7929
|
+
}
|
|
7930
|
+
setStateInSession(mensaje, state) {
|
|
7931
|
+
this.message = mensaje;
|
|
7932
|
+
if (!isPlatformBrowser(this.platformId))
|
|
7933
|
+
return;
|
|
7934
|
+
try {
|
|
7935
|
+
sessionStorage.setItem('state', state);
|
|
7936
|
+
}
|
|
7937
|
+
catch { }
|
|
7938
|
+
try {
|
|
7939
|
+
localStorage.setItem('state', state);
|
|
7940
|
+
}
|
|
7941
|
+
catch { }
|
|
7942
|
+
try {
|
|
7943
|
+
sessionStorage.setItem('modalnews', 'false');
|
|
7944
|
+
}
|
|
7945
|
+
catch { }
|
|
7946
|
+
}
|
|
7947
|
+
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 });
|
|
7948
|
+
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" }] });
|
|
7949
|
+
}
|
|
7950
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: RedsysCatchEcComponent, decorators: [{
|
|
7951
|
+
type: Component,
|
|
7952
|
+
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"] }]
|
|
7953
|
+
}], ctorParameters: () => [{ type: i2.ActivatedRoute }, { type: i2.Router }, { type: CheckoutService }, { type: i0.Renderer2 }, { type: i0.ElementRef }, { type: Document, decorators: [{
|
|
7954
|
+
type: Inject,
|
|
7955
|
+
args: [DOCUMENT]
|
|
7956
|
+
}] }, { type: undefined, decorators: [{
|
|
7957
|
+
type: Inject,
|
|
7958
|
+
args: [PLATFORM_ID]
|
|
7959
|
+
}] }] });
|
|
7960
|
+
|
|
7404
7961
|
// export * from './rating-ec/rating-ec.component';
|
|
7405
7962
|
|
|
7406
7963
|
class BlockFormContactEcComponent extends BlockEcComponent {
|
|
@@ -7676,6 +8233,7 @@ class LoginFormEcComponent {
|
|
|
7676
8233
|
_formBuilder = inject(FormBuilder);
|
|
7677
8234
|
_toastService = inject(ToastService);
|
|
7678
8235
|
_router = inject(Router);
|
|
8236
|
+
showPassword = false;
|
|
7679
8237
|
/**
|
|
7680
8238
|
* Parametro para indicar si tras loguear
|
|
7681
8239
|
* debe redireccionar o no.
|
|
@@ -7764,6 +8322,9 @@ class LoginFormEcComponent {
|
|
|
7764
8322
|
? resolverFunction()
|
|
7765
8323
|
: this._router.navigateByUrl(this.redirectTo);
|
|
7766
8324
|
}
|
|
8325
|
+
togglePassword() {
|
|
8326
|
+
this.showPassword = !this.showPassword;
|
|
8327
|
+
}
|
|
7767
8328
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: LoginFormEcComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
7768
8329
|
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"] }] });
|
|
7769
8330
|
}
|
|
@@ -7863,6 +8424,7 @@ class RegisterFormEcComponent {
|
|
|
7863
8424
|
_analyticsService = inject(AnalyticsService);
|
|
7864
8425
|
_formBuilder = inject(FormBuilder);
|
|
7865
8426
|
channelConfigService = inject(ChannelService);
|
|
8427
|
+
showPassword = false;
|
|
7866
8428
|
/**
|
|
7867
8429
|
* Indica si debe redireccionar o se queda en la misma pantalla
|
|
7868
8430
|
*/
|
|
@@ -7978,6 +8540,9 @@ class RegisterFormEcComponent {
|
|
|
7978
8540
|
this.register_loading = false;
|
|
7979
8541
|
}
|
|
7980
8542
|
}
|
|
8543
|
+
togglePassword() {
|
|
8544
|
+
this.showPassword = !this.showPassword;
|
|
8545
|
+
}
|
|
7981
8546
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: RegisterFormEcComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
7982
8547
|
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" }] });
|
|
7983
8548
|
}
|
|
@@ -8132,20 +8697,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
|
|
|
8132
8697
|
type: Output
|
|
8133
8698
|
}] } });
|
|
8134
8699
|
|
|
8135
|
-
class ComponentHelper {
|
|
8136
|
-
constructor() {
|
|
8137
|
-
this.ecOnConstruct();
|
|
8138
|
-
}
|
|
8139
|
-
ecOnInit = (params = {}) => {
|
|
8140
|
-
};
|
|
8141
|
-
ecOnConstruct = (params = {}) => {
|
|
8142
|
-
};
|
|
8143
|
-
hasParams = (params, searched) => params && params != null && params.find(param => param['code']?.toLowerCase().includes(searched.toLowerCase())) !== undefined;
|
|
8144
|
-
navigateOnRouter(router, url) {
|
|
8145
|
-
router.navigateByUrl(`/${url}`);
|
|
8146
|
-
}
|
|
8147
|
-
}
|
|
8148
|
-
|
|
8149
8700
|
class PasswordResetEcComponent extends ComponentHelper {
|
|
8150
8701
|
authService;
|
|
8151
8702
|
toastr;
|
|
@@ -8355,6 +8906,14 @@ class FiltersEcComponent {
|
|
|
8355
8906
|
});
|
|
8356
8907
|
}) ?? false;
|
|
8357
8908
|
}
|
|
8909
|
+
/**
|
|
8910
|
+
* Verifica si una categoría tiene la propiedad isVisible y está marcada como visible
|
|
8911
|
+
* @param category - La categoría a verificar
|
|
8912
|
+
* @returns true si la categoría es visible, false en caso contrario
|
|
8913
|
+
*/
|
|
8914
|
+
hasVisibleProperty(category) {
|
|
8915
|
+
return category.isVisible === true;
|
|
8916
|
+
}
|
|
8358
8917
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: FiltersEcComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
8359
8918
|
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: [""] });
|
|
8360
8919
|
}
|
|
@@ -8435,6 +8994,7 @@ class ProductDetailEcComponent {
|
|
|
8435
8994
|
routeSubscription;
|
|
8436
8995
|
route = inject(ActivatedRoute);
|
|
8437
8996
|
currentProductId;
|
|
8997
|
+
_router = inject(Router);
|
|
8438
8998
|
ngOnDestroy() {
|
|
8439
8999
|
this.routeSubscription?.unsubscribe();
|
|
8440
9000
|
}
|
|
@@ -8496,11 +9056,12 @@ class ProductDetailEcComponent {
|
|
|
8496
9056
|
}
|
|
8497
9057
|
updateMetaTags(product) {
|
|
8498
9058
|
const descripcionLimpia = he.decode(product.description || '').replace(/<[^>]*>/g, ' ').replace(/\s+/g, ' ').trim();
|
|
8499
|
-
const currentUrl = this._consts.
|
|
9059
|
+
const currentUrl = this._consts.getFrontendUrl() + this._router.url; // URL absoluta del producto
|
|
9060
|
+
const imageUrl = this._consts.getAbsoluteImageUrl(Array.isArray(product.picturesdefault) ? product.picturesdefault[0] : product.picturesdefault);
|
|
8500
9061
|
this._meta.updateTag({ property: 'og:title', content: product.name || '' });
|
|
8501
9062
|
this._meta.updateTag({ property: 'og:description', content: descripcionLimpia || '' });
|
|
8502
|
-
this._meta.updateTag({ property: 'og:image', content:
|
|
8503
|
-
this._meta.updateTag({ property: 'og:url', content: currentUrl });
|
|
9063
|
+
this._meta.updateTag({ property: 'og:image', content: imageUrl || '' });
|
|
9064
|
+
// this._meta.updateTag({ property: 'og:url', content: currentUrl });
|
|
8504
9065
|
this._meta.updateTag({ property: 'og:type', content: 'product' });
|
|
8505
9066
|
}
|
|
8506
9067
|
decodeHtml(html) {
|
|
@@ -8736,6 +9297,7 @@ class CartItemEcComponent {
|
|
|
8736
9297
|
_cartService = inject(CartService);
|
|
8737
9298
|
_toastService = inject(ToastService);
|
|
8738
9299
|
_constants = inject(CoreConstantsService);
|
|
9300
|
+
parametersService = inject(ParametersService);
|
|
8739
9301
|
mediaUrl = this._constants.mediaUrl();
|
|
8740
9302
|
quantity = 0;
|
|
8741
9303
|
variantsToShow = ['TALLA', 'COLOR'];
|
|
@@ -8746,28 +9308,56 @@ class CartItemEcComponent {
|
|
|
8746
9308
|
// console.log(this.item, this.mediaUrl);
|
|
8747
9309
|
}
|
|
8748
9310
|
updateQuantity(stock) {
|
|
9311
|
+
const originalQuantity = this.item.quantity; // Guardar cantidad original
|
|
8749
9312
|
if (this.quantity > 0 && this.quantity <= stock) {
|
|
8750
|
-
this._cartService.updateItemQuantity(this.item, this.quantity);
|
|
9313
|
+
const success = this._cartService.updateItemQuantity(this.item, this.quantity);
|
|
9314
|
+
// Si la validación falló, restaurar la cantidad original
|
|
9315
|
+
if (!success) {
|
|
9316
|
+
this.quantity = originalQuantity;
|
|
9317
|
+
}
|
|
8751
9318
|
}
|
|
8752
9319
|
else {
|
|
8753
|
-
|
|
9320
|
+
// Restaurar cantidad original si está fuera de rango
|
|
9321
|
+
this.quantity = originalQuantity;
|
|
8754
9322
|
this._toastService.show('out-of-stock-actually');
|
|
8755
9323
|
}
|
|
8756
9324
|
}
|
|
8757
|
-
|
|
9325
|
+
plus(stock, value = 1) {
|
|
8758
9326
|
if (this.isQuantityUpdating)
|
|
8759
9327
|
return;
|
|
8760
9328
|
this.isQuantityUpdating = true;
|
|
8761
|
-
let
|
|
8762
|
-
|
|
9329
|
+
let newQuantity = Number(this.quantity) + value; // Forzar a número
|
|
9330
|
+
// Verificar stock ANTES de llamar al servicio
|
|
9331
|
+
if (newQuantity > stock) {
|
|
9332
|
+
this._toastService.show('out-of-stock-actually');
|
|
9333
|
+
setTimeout(() => { this.isQuantityUpdating = false; }, 1000);
|
|
9334
|
+
return;
|
|
9335
|
+
}
|
|
9336
|
+
const success = this._cartService.updateItemQuantity(this.item, newQuantity);
|
|
9337
|
+
// Solo actualizar el input si la operación fue exitosa
|
|
9338
|
+
if (success) {
|
|
9339
|
+
this.quantity = newQuantity;
|
|
9340
|
+
}
|
|
9341
|
+
// Si success es false, NO mostrar mensaje aquí porque ya lo mostró validateQuantity()
|
|
8763
9342
|
setTimeout(() => { this.isQuantityUpdating = false; }, 1000);
|
|
8764
9343
|
}
|
|
8765
|
-
|
|
9344
|
+
less(stock, value = 1) {
|
|
8766
9345
|
if (this.isQuantityUpdating)
|
|
8767
9346
|
return;
|
|
8768
9347
|
this.isQuantityUpdating = true;
|
|
8769
|
-
let
|
|
8770
|
-
|
|
9348
|
+
let newQuantity = Number(this.quantity) - value; // Forzar a número
|
|
9349
|
+
// Verificar cantidad mínima ANTES de llamar al servicio
|
|
9350
|
+
if (newQuantity <= 0) {
|
|
9351
|
+
// No permitir cantidades menores o iguales a 0
|
|
9352
|
+
setTimeout(() => { this.isQuantityUpdating = false; }, 1000);
|
|
9353
|
+
return;
|
|
9354
|
+
}
|
|
9355
|
+
const success = this._cartService.updateItemQuantity(this.item, newQuantity);
|
|
9356
|
+
// Solo actualizar el input si la operación fue exitosa
|
|
9357
|
+
if (success) {
|
|
9358
|
+
this.quantity = newQuantity;
|
|
9359
|
+
}
|
|
9360
|
+
// Si success es false, NO mostrar mensaje aquí porque ya lo mostró validateQuantity()
|
|
8771
9361
|
setTimeout(() => { this.isQuantityUpdating = false; }, 1000);
|
|
8772
9362
|
}
|
|
8773
9363
|
deleteCartItem() {
|
|
@@ -8822,6 +9412,9 @@ class CartItemEcComponent {
|
|
|
8822
9412
|
}
|
|
8823
9413
|
return false; // Solo se ejecuta si no se cumple la condición
|
|
8824
9414
|
}
|
|
9415
|
+
// PARAMETROS
|
|
9416
|
+
parameters$ = this.parametersService.getParameters();
|
|
9417
|
+
hasParams = this.parametersService.hasParams;
|
|
8825
9418
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: CartItemEcComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
8826
9419
|
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"] }] });
|
|
8827
9420
|
}
|
|
@@ -9333,108 +9926,174 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
|
|
|
9333
9926
|
class MpRedirectEcComponent {
|
|
9334
9927
|
_paymentService = inject(PaymentService);
|
|
9335
9928
|
_toastService = inject(ToastService);
|
|
9929
|
+
platformId = inject(PLATFORM_ID);
|
|
9930
|
+
finished = false;
|
|
9336
9931
|
method = null;
|
|
9337
9932
|
total_amount = 0;
|
|
9338
9933
|
allData;
|
|
9339
9934
|
ready = new EventEmitter();
|
|
9340
9935
|
preference;
|
|
9341
|
-
loading = false;
|
|
9342
9936
|
url;
|
|
9343
|
-
|
|
9344
|
-
|
|
9345
|
-
|
|
9346
|
-
|
|
9347
|
-
|
|
9348
|
-
|
|
9937
|
+
// Fases de UI
|
|
9938
|
+
phase = 'idle';
|
|
9939
|
+
get isIdle() { return this.phase === 'idle'; }
|
|
9940
|
+
get isPaying() { return this.phase === 'paying'; }
|
|
9941
|
+
get isFinalizing() { return this.phase === 'finalizing'; }
|
|
9942
|
+
ventana = null;
|
|
9943
|
+
windowRef;
|
|
9944
|
+
sid = '';
|
|
9945
|
+
bc;
|
|
9946
|
+
pollTimer;
|
|
9947
|
+
pollStartedAt = 0;
|
|
9948
|
+
ngOnInit() {
|
|
9349
9949
|
if (isPlatformBrowser(this.platformId)) {
|
|
9350
|
-
this.
|
|
9351
|
-
|
|
9950
|
+
this.windowRef = window;
|
|
9951
|
+
if ('BroadcastChannel' in window) {
|
|
9952
|
+
this.bc = new BroadcastChannel('mp_payment');
|
|
9953
|
+
this.bc.onmessage = (e) => this.onMpMessage(e?.data);
|
|
9954
|
+
}
|
|
9955
|
+
window.addEventListener('storage', this.onStorage);
|
|
9956
|
+
window.addEventListener('message', this.onWindowMessage);
|
|
9352
9957
|
}
|
|
9353
|
-
}
|
|
9354
|
-
ngOnInit() {
|
|
9355
9958
|
this.getPreference();
|
|
9356
9959
|
}
|
|
9960
|
+
ngOnDestroy() {
|
|
9961
|
+
if (!isPlatformBrowser(this.platformId))
|
|
9962
|
+
return;
|
|
9963
|
+
this.bc?.close();
|
|
9964
|
+
window.removeEventListener('storage', this.onStorage);
|
|
9965
|
+
window.removeEventListener('message', this.onWindowMessage);
|
|
9966
|
+
if (this.pollTimer)
|
|
9967
|
+
clearInterval(this.pollTimer);
|
|
9968
|
+
}
|
|
9969
|
+
/** Cancela manualmente el pago y finaliza el flujo con estado "cancel". */
|
|
9357
9970
|
clickClose = () => {
|
|
9358
|
-
|
|
9359
|
-
|
|
9971
|
+
if (this.finished)
|
|
9972
|
+
return;
|
|
9973
|
+
this.finishWithState('cancel');
|
|
9360
9974
|
};
|
|
9975
|
+
/**
|
|
9976
|
+
* Inicia el pago abriendo `init_point` en una ventana/pestaña nueva.
|
|
9977
|
+
* Genera un SID y lo persiste para casar la respuesta del catch.
|
|
9978
|
+
* Si el popup es bloqueado, hace fallback navegando en la misma pestaña.
|
|
9979
|
+
*/
|
|
9361
9980
|
iniciar = () => {
|
|
9362
|
-
this.
|
|
9363
|
-
|
|
9364
|
-
this.
|
|
9365
|
-
this.
|
|
9366
|
-
|
|
9367
|
-
|
|
9368
|
-
|
|
9369
|
-
|
|
9370
|
-
this.
|
|
9371
|
-
|
|
9372
|
-
|
|
9373
|
-
|
|
9374
|
-
|
|
9375
|
-
|
|
9376
|
-
|
|
9377
|
-
|
|
9378
|
-
|
|
9379
|
-
|
|
9380
|
-
|
|
9381
|
-
this.
|
|
9382
|
-
this.
|
|
9383
|
-
|
|
9384
|
-
}
|
|
9385
|
-
if (state == 'failure') {
|
|
9386
|
-
this.ventana?.close();
|
|
9387
|
-
this.processError('');
|
|
9388
|
-
return;
|
|
9389
|
-
}
|
|
9390
|
-
if (state == 'cancel') {
|
|
9391
|
-
this.ventana?.close();
|
|
9392
|
-
this.processError('Se cancelo el pago con mercado pago');
|
|
9981
|
+
if (!isPlatformBrowser(this.platformId) || !this.windowRef || !this.url)
|
|
9982
|
+
return;
|
|
9983
|
+
this.phase = 'paying';
|
|
9984
|
+
this.sid = this.genSid();
|
|
9985
|
+
const url = new URL(this.url);
|
|
9986
|
+
localStorage.setItem('mp:sid', this.sid);
|
|
9987
|
+
this.ventana = this.windowRef.open(this.url, '_blank');
|
|
9988
|
+
// popup bloqueado → fallback a navegación en misma pestaña
|
|
9989
|
+
if (!this.ventana || this.ventana.closed) {
|
|
9990
|
+
this.windowRef.location.href = this.url;
|
|
9991
|
+
return;
|
|
9992
|
+
}
|
|
9993
|
+
// polling de último recurso (hasta 10 minutos)
|
|
9994
|
+
this.pollStartedAt = Date.now();
|
|
9995
|
+
if (this.pollTimer)
|
|
9996
|
+
clearInterval(this.pollTimer);
|
|
9997
|
+
this.pollTimer = setInterval(() => {
|
|
9998
|
+
if (Date.now() - this.pollStartedAt > 10 * 60 * 1000) {
|
|
9999
|
+
clearInterval(this.pollTimer);
|
|
10000
|
+
this.pollTimer = null;
|
|
10001
|
+
this.phase = 'idle';
|
|
10002
|
+
this.processError('Tiempo de espera agotado al procesar el pago.');
|
|
9393
10003
|
return;
|
|
9394
10004
|
}
|
|
9395
|
-
this.
|
|
9396
|
-
|
|
10005
|
+
this.checkLocalStorageOnce();
|
|
10006
|
+
}, 1000);
|
|
10007
|
+
};
|
|
10008
|
+
onWindowMessage = (event) => {
|
|
10009
|
+
const data = event?.data;
|
|
10010
|
+
this.onMpMessage(data);
|
|
10011
|
+
};
|
|
10012
|
+
onStorage = (e) => {
|
|
10013
|
+
if (!e.key || !this.sid)
|
|
9397
10014
|
return;
|
|
10015
|
+
if (e.key === `mp:state:${this.sid}` && e.newValue) {
|
|
10016
|
+
const state = e.newValue;
|
|
10017
|
+
this.finishWithState(state);
|
|
9398
10018
|
}
|
|
9399
|
-
setTimeout(() => {
|
|
9400
|
-
this.callState();
|
|
9401
|
-
}, 5000);
|
|
9402
10019
|
};
|
|
9403
|
-
|
|
9404
|
-
|
|
9405
|
-
|
|
10020
|
+
onMpMessage = (data) => {
|
|
10021
|
+
if (!data || data.type !== 'mp:state')
|
|
10022
|
+
return;
|
|
10023
|
+
if (data.sid !== this.sid)
|
|
10024
|
+
return;
|
|
10025
|
+
this.finishWithState(data.state);
|
|
9406
10026
|
};
|
|
10027
|
+
checkLocalStorageOnce() {
|
|
10028
|
+
if (!this.sid)
|
|
10029
|
+
return;
|
|
10030
|
+
const state = localStorage.getItem(`mp:state:${this.sid}`);
|
|
10031
|
+
if (state)
|
|
10032
|
+
this.finishWithState(state);
|
|
10033
|
+
}
|
|
10034
|
+
/** Cierra el flujo de pago con el estado final y notifica al padre. */
|
|
10035
|
+
finishWithState(state) {
|
|
10036
|
+
if (this.finished)
|
|
10037
|
+
return;
|
|
10038
|
+
this.finished = true;
|
|
10039
|
+
if (this.pollTimer)
|
|
10040
|
+
clearInterval(this.pollTimer);
|
|
10041
|
+
this.pollTimer = null;
|
|
10042
|
+
localStorage.removeItem(`mp:state:${this.sid}`);
|
|
10043
|
+
localStorage.removeItem('mp:sid');
|
|
10044
|
+
localStorage.removeItem('state');
|
|
10045
|
+
try {
|
|
10046
|
+
this.ventana && !this.ventana.closed && this.ventana.close();
|
|
10047
|
+
}
|
|
10048
|
+
catch { }
|
|
10049
|
+
this.ventana = null;
|
|
10050
|
+
if (state === 'success' || state === 'pending') {
|
|
10051
|
+
this.phase = 'finalizing';
|
|
10052
|
+
this.ready.emit(true);
|
|
10053
|
+
}
|
|
10054
|
+
else if (state === 'failure' || state === 'cancel') {
|
|
10055
|
+
this.phase = 'idle';
|
|
10056
|
+
this._toastService.show(state === 'cancel' ? 'Se canceló el pago con Mercado Pago' : 'payment-error');
|
|
10057
|
+
}
|
|
10058
|
+
else {
|
|
10059
|
+
this.phase = 'idle';
|
|
10060
|
+
this._toastService.show('payment-error');
|
|
10061
|
+
}
|
|
10062
|
+
}
|
|
10063
|
+
genSid() {
|
|
10064
|
+
return `mp_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
|
|
10065
|
+
}
|
|
10066
|
+
processError = (msg) => {
|
|
10067
|
+
this._toastService.show(msg || 'payment-error');
|
|
10068
|
+
};
|
|
10069
|
+
/** Limpia posibles residuos de estado en storages. */
|
|
9407
10070
|
clearStorageState = () => {
|
|
9408
|
-
|
|
9409
|
-
|
|
10071
|
+
if (!isPlatformBrowser(this.platformId))
|
|
10072
|
+
return;
|
|
10073
|
+
localStorage.removeItem('state');
|
|
10074
|
+
const sid = localStorage.getItem('mp:sid');
|
|
10075
|
+
if (sid)
|
|
10076
|
+
localStorage.removeItem(`mp:state:${sid}`);
|
|
10077
|
+
};
|
|
10078
|
+
/** Obtiene la preferencia e inicializa `url` (init_point). */
|
|
10079
|
+
getPreference = () => {
|
|
10080
|
+
this._paymentService.getPreference(this.allData).then((res) => {
|
|
10081
|
+
this.preference = res;
|
|
10082
|
+
this.url = this.preference?.init_point;
|
|
10083
|
+
this.renderMP(this.preference);
|
|
10084
|
+
}, () => this.setError('operation-error'));
|
|
9410
10085
|
};
|
|
9411
|
-
getPreference = () => this._paymentService.getPreference(this.allData).then(res => {
|
|
9412
|
-
this.preference = res;
|
|
9413
|
-
this.url = this.preference.init_point;
|
|
9414
|
-
this.renderMP(this.preference);
|
|
9415
|
-
}, err => this.setError('operation-error'));
|
|
9416
10086
|
setError = (message) => {
|
|
9417
10087
|
//this.error = message;
|
|
9418
10088
|
};
|
|
9419
|
-
renderMP = (
|
|
9420
|
-
this.window?.addEventListener("message", (event) => {
|
|
9421
|
-
if (event.origin !== 'https://www.mercadopago.com.ar' || !event.data.type) {
|
|
9422
|
-
return;
|
|
9423
|
-
}
|
|
9424
|
-
let dataType = event.data.type;
|
|
9425
|
-
if (dataType === 'submit') {
|
|
9426
|
-
const paymentData = event.data.value;
|
|
9427
|
-
return;
|
|
9428
|
-
}
|
|
9429
|
-
});
|
|
9430
|
-
};
|
|
10089
|
+
renderMP = (_pref) => { };
|
|
9431
10090
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MpRedirectEcComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
9432
|
-
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\
|
|
10091
|
+
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" }] });
|
|
9433
10092
|
}
|
|
9434
10093
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MpRedirectEcComponent, decorators: [{
|
|
9435
10094
|
type: Component,
|
|
9436
|
-
args: [{ selector: 'app-mp-redirect-ec', standalone: true, imports: [LoadingFullEcComponent], template: "<div class=\"text-center\">\r\n\t@if(url){\r\n\
|
|
9437
|
-
}],
|
|
10095
|
+
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>" }]
|
|
10096
|
+
}], propDecorators: { method: [{
|
|
9438
10097
|
type: Input
|
|
9439
10098
|
}], total_amount: [{
|
|
9440
10099
|
type: Input
|
|
@@ -9677,11 +10336,11 @@ class DecidirEcComponent extends ComponentHelper {
|
|
|
9677
10336
|
obj.style.width = obj.contentWindow.document.body.scrollWidth + 'px';
|
|
9678
10337
|
};
|
|
9679
10338
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DecidirEcComponent, deps: [{ token: i0.Renderer2 }, { token: ConnectionService }, { token: ToastService }, { token: CoreConstantsService }, { token: ApiConstantsService }, { token: CartService }, { token: i2.ActivatedRoute }, { token: i1$4.DomSanitizer }, { token: ParametersService }], target: i0.ɵɵFactoryTarget.Component });
|
|
9680
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.13", type: DecidirEcComponent, isStandalone: true, selector: "app-decidir-ec", inputs: { paymentServiceInst: "paymentServiceInst", method: "method", total_amount: "total_amount", allData: "allData", user_data: "user_data" }, outputs: { ready: "ready" }, usesInheritance: true, usesOnChanges: true, ngImport: i0, template: "<div class=\"text-center\">\r\n <h3>Continuar con el pago en Decidir</h3>\r\n @if (method) {\r\n <p class=\"px-5\">{{ method.description }}</p>\r\n <p class=\"px-5\">{{ method.instructions }}</p>\r\n }\r\n @if (!loading) {\r\n <button class=\"btn btn-outline-secondary comprar\" (click)=\"openModal()\">Pagar</button>\r\n } @else {\r\n <div class=\"d-flex flex-column jusitfy-content-center align-items-center mt-2\">\r\n <app-loading-full-ec></app-loading-full-ec>\r\n </div>\r\n }\r\n </div>\r\n\r\n@if (showModal) {\r\n<div class=\"modal-backdrop\" (click)=\"clickClose()\">\r\n <div class=\"modal-dialog modal-lg\" (click)=\"$event.stopPropagation()\">\r\n <div class=\"modal-content\">\r\n\r\n <div class=\"modal-body\">\r\n <div class=\"payment-container\">\r\n \r\n <!-- Iframe del formulario de decidir -->\r\n <div class=\"iframe-container\">\r\n <iframe [src]=\"url\" frameborder=\"0\" class=\"payment-iframe\"></iframe>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n}\r\n", styles: [".modal-backdrop{position:fixed;top:0;left:0;width:100%;height:100%;background-color:#0009;display:flex;justify-content:center;align-items:center;z-index:1050
|
|
10339
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.13", type: DecidirEcComponent, isStandalone: true, selector: "app-decidir-ec", inputs: { paymentServiceInst: "paymentServiceInst", method: "method", total_amount: "total_amount", allData: "allData", user_data: "user_data" }, outputs: { ready: "ready" }, usesInheritance: true, usesOnChanges: true, ngImport: i0, template: "<div class=\"text-center\">\r\n <h3>Continuar con el pago en Decidir</h3>\r\n @if (method) {\r\n <p class=\"px-5\">{{ method.description }}</p>\r\n <p class=\"px-5\">{{ method.instructions }}</p>\r\n }\r\n @if (!loading) {\r\n <button class=\"btn btn-outline-secondary comprar\" (click)=\"openModal()\">Pagar</button>\r\n } @else {\r\n <div class=\"d-flex flex-column jusitfy-content-center align-items-center mt-2\">\r\n <app-loading-full-ec></app-loading-full-ec>\r\n </div>\r\n }\r\n </div>\r\n\r\n@if (showModal) {\r\n<div class=\"modal-backdrop\" (click)=\"clickClose()\">\r\n <div class=\"modal-dialog modal-lg\" (click)=\"$event.stopPropagation()\">\r\n <div class=\"modal-content\">\r\n\r\n <div class=\"modal-body\">\r\n <div class=\"payment-container\">\r\n \r\n <!-- Iframe del formulario de decidir -->\r\n <div class=\"iframe-container\">\r\n <iframe [src]=\"url\" frameborder=\"0\" class=\"payment-iframe\"></iframe>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n}\r\n", styles: [".modal-backdrop{position:fixed;top:0;left:0;width:100%;height:100%;background-color:#0009;display:flex;justify-content:center;align-items:center;z-index:1050;backdrop-filter:blur(2px)}.modal-dialog{max-width:90%;max-height:90%;width:800px;background:#fff;border-radius:16px;overflow:hidden;box-shadow:0 20px 60px #0000004d;animation:modalFadeIn .3s ease-out}.modal-dialog.modal-lg{max-width:900px;width:90%}@keyframes modalFadeIn{0%{opacity:0;transform:scale(.9) translateY(-20px)}to{opacity:1;transform:scale(1) translateY(0)}}.modal-content{display:flex;flex-direction:column;height:100%;border:none;border-radius:16px;background:#fff}.modal-header{padding:1.5rem 2rem;border-bottom:1px solid #e9ecef;background:linear-gradient(135deg,#f8f9fa,#e9ecef);display:flex;justify-content:space-between;align-items:center;position:relative}.modal-header:after{content:\"\";position:absolute;bottom:0;left:0;right:0;height:1px;background:linear-gradient(90deg,transparent 0%,#dee2e6 50%,transparent 100%)}.modal-header .modal-title{margin:0;font-size:1.5rem;font-weight:700;color:#2c3e50;text-shadow:0 1px 2px rgba(0,0,0,.1)}.modal-header .btn-close{background:none;border:none;font-size:1.8rem;color:#6c757d;cursor:pointer;padding:0;width:35px;height:35px;display:flex;align-items:center;justify-content:center;border-radius:50%;transition:all .3s ease;position:relative}.modal-header .btn-close:hover{background-color:#dc3545;color:#fff;transform:rotate(90deg);box-shadow:0 4px 12px #dc35454d}.modal-header .btn-close:active{transform:rotate(90deg) scale(.95)}.modal-body{padding:0;flex:1;overflow:hidden;background:#fff}.payment-container{height:100%;display:flex;flex-direction:column;background:#fff}.cards-accepted{padding:1.5rem 2rem;text-align:center;border-bottom:1px solid #e9ecef;background:linear-gradient(135deg,#fff,#f8f9fa);position:relative}.cards-accepted:after{content:\"\";position:absolute;bottom:0;left:5%;right:5%;height:1px;background:linear-gradient(90deg,transparent 0%,#dee2e6 50%,transparent 100%)}.cards-accepted .cards-img{height:45px;max-width:100%;object-fit:contain;filter:drop-shadow(0 2px 4px rgba(0,0,0,.1));transition:transform .2s ease}.cards-accepted .cards-img:hover{transform:scale(1.05)}.iframe-container{flex:1;padding:1.5rem;background:#fff;position:relative}.iframe-container:before{content:\"\";position:absolute;top:0;left:1.5rem;right:1.5rem;height:1px;background:linear-gradient(90deg,transparent 0%,#e9ecef 50%,transparent 100%)}.payment-iframe{width:100%;height:620px;border:none;border-radius:12px;background:#fff;box-shadow:inset 0 2px 8px #0000000d}.half-width{width:49%!important}.ml-1{margin-left:1%}#card-form{height:450px}.iframeStyle{height:520px;width:100%}@media only screen and (max-width: 1024px){.modal-dialog,.modal-dialog.modal-lg{max-width:95%;width:95%}.payment-iframe{height:650px}.modal-header{padding:1rem 1.5rem}.modal-header .modal-title{font-size:1.125rem}.cards-accepted{padding:.75rem 1.5rem}}@media only screen and (max-width: 680px){.modal-dialog{max-width:98%;width:98%;margin:1rem;max-height:calc(100vh - 2rem)}.modal-dialog.modal-lg{max-width:98%;width:98%}.payment-iframe{height:550px}.modal-header{padding:1rem}.modal-header .modal-title{font-size:1rem}.modal-header .btn-close{width:28px;height:28px;font-size:1.25rem}.cards-accepted{padding:.5rem 1rem}.cards-accepted .cards-img{height:35px}.iframe-container{padding:.5rem}}@media only screen and (max-width: 480px){.modal-dialog{margin:.5rem;max-height:calc(100vh - 1rem);border-radius:8px}.modal-content{border-radius:8px}.payment-iframe{height:500px}.cards-accepted .cards-img{height:30px}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "component", type: LoadingFullEcComponent, selector: "app-loading-full-ec" }] });
|
|
9681
10340
|
}
|
|
9682
10341
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DecidirEcComponent, decorators: [{
|
|
9683
10342
|
type: Component,
|
|
9684
|
-
args: [{ selector: 'app-decidir-ec', standalone: true, imports: [CommonModule, FormsModule, LoadingFullEcComponent], template: "<div class=\"text-center\">\r\n <h3>Continuar con el pago en Decidir</h3>\r\n @if (method) {\r\n <p class=\"px-5\">{{ method.description }}</p>\r\n <p class=\"px-5\">{{ method.instructions }}</p>\r\n }\r\n @if (!loading) {\r\n <button class=\"btn btn-outline-secondary comprar\" (click)=\"openModal()\">Pagar</button>\r\n } @else {\r\n <div class=\"d-flex flex-column jusitfy-content-center align-items-center mt-2\">\r\n <app-loading-full-ec></app-loading-full-ec>\r\n </div>\r\n }\r\n </div>\r\n\r\n@if (showModal) {\r\n<div class=\"modal-backdrop\" (click)=\"clickClose()\">\r\n <div class=\"modal-dialog modal-lg\" (click)=\"$event.stopPropagation()\">\r\n <div class=\"modal-content\">\r\n\r\n <div class=\"modal-body\">\r\n <div class=\"payment-container\">\r\n \r\n <!-- Iframe del formulario de decidir -->\r\n <div class=\"iframe-container\">\r\n <iframe [src]=\"url\" frameborder=\"0\" class=\"payment-iframe\"></iframe>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n}\r\n", styles: [".modal-backdrop{position:fixed;top:0;left:0;width:100%;height:100%;background-color:#0009;display:flex;justify-content:center;align-items:center;z-index:1050
|
|
10343
|
+
args: [{ selector: 'app-decidir-ec', standalone: true, imports: [CommonModule, FormsModule, LoadingFullEcComponent], template: "<div class=\"text-center\">\r\n <h3>Continuar con el pago en Decidir</h3>\r\n @if (method) {\r\n <p class=\"px-5\">{{ method.description }}</p>\r\n <p class=\"px-5\">{{ method.instructions }}</p>\r\n }\r\n @if (!loading) {\r\n <button class=\"btn btn-outline-secondary comprar\" (click)=\"openModal()\">Pagar</button>\r\n } @else {\r\n <div class=\"d-flex flex-column jusitfy-content-center align-items-center mt-2\">\r\n <app-loading-full-ec></app-loading-full-ec>\r\n </div>\r\n }\r\n </div>\r\n\r\n@if (showModal) {\r\n<div class=\"modal-backdrop\" (click)=\"clickClose()\">\r\n <div class=\"modal-dialog modal-lg\" (click)=\"$event.stopPropagation()\">\r\n <div class=\"modal-content\">\r\n\r\n <div class=\"modal-body\">\r\n <div class=\"payment-container\">\r\n \r\n <!-- Iframe del formulario de decidir -->\r\n <div class=\"iframe-container\">\r\n <iframe [src]=\"url\" frameborder=\"0\" class=\"payment-iframe\"></iframe>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n}\r\n", styles: [".modal-backdrop{position:fixed;top:0;left:0;width:100%;height:100%;background-color:#0009;display:flex;justify-content:center;align-items:center;z-index:1050;backdrop-filter:blur(2px)}.modal-dialog{max-width:90%;max-height:90%;width:800px;background:#fff;border-radius:16px;overflow:hidden;box-shadow:0 20px 60px #0000004d;animation:modalFadeIn .3s ease-out}.modal-dialog.modal-lg{max-width:900px;width:90%}@keyframes modalFadeIn{0%{opacity:0;transform:scale(.9) translateY(-20px)}to{opacity:1;transform:scale(1) translateY(0)}}.modal-content{display:flex;flex-direction:column;height:100%;border:none;border-radius:16px;background:#fff}.modal-header{padding:1.5rem 2rem;border-bottom:1px solid #e9ecef;background:linear-gradient(135deg,#f8f9fa,#e9ecef);display:flex;justify-content:space-between;align-items:center;position:relative}.modal-header:after{content:\"\";position:absolute;bottom:0;left:0;right:0;height:1px;background:linear-gradient(90deg,transparent 0%,#dee2e6 50%,transparent 100%)}.modal-header .modal-title{margin:0;font-size:1.5rem;font-weight:700;color:#2c3e50;text-shadow:0 1px 2px rgba(0,0,0,.1)}.modal-header .btn-close{background:none;border:none;font-size:1.8rem;color:#6c757d;cursor:pointer;padding:0;width:35px;height:35px;display:flex;align-items:center;justify-content:center;border-radius:50%;transition:all .3s ease;position:relative}.modal-header .btn-close:hover{background-color:#dc3545;color:#fff;transform:rotate(90deg);box-shadow:0 4px 12px #dc35454d}.modal-header .btn-close:active{transform:rotate(90deg) scale(.95)}.modal-body{padding:0;flex:1;overflow:hidden;background:#fff}.payment-container{height:100%;display:flex;flex-direction:column;background:#fff}.cards-accepted{padding:1.5rem 2rem;text-align:center;border-bottom:1px solid #e9ecef;background:linear-gradient(135deg,#fff,#f8f9fa);position:relative}.cards-accepted:after{content:\"\";position:absolute;bottom:0;left:5%;right:5%;height:1px;background:linear-gradient(90deg,transparent 0%,#dee2e6 50%,transparent 100%)}.cards-accepted .cards-img{height:45px;max-width:100%;object-fit:contain;filter:drop-shadow(0 2px 4px rgba(0,0,0,.1));transition:transform .2s ease}.cards-accepted .cards-img:hover{transform:scale(1.05)}.iframe-container{flex:1;padding:1.5rem;background:#fff;position:relative}.iframe-container:before{content:\"\";position:absolute;top:0;left:1.5rem;right:1.5rem;height:1px;background:linear-gradient(90deg,transparent 0%,#e9ecef 50%,transparent 100%)}.payment-iframe{width:100%;height:620px;border:none;border-radius:12px;background:#fff;box-shadow:inset 0 2px 8px #0000000d}.half-width{width:49%!important}.ml-1{margin-left:1%}#card-form{height:450px}.iframeStyle{height:520px;width:100%}@media only screen and (max-width: 1024px){.modal-dialog,.modal-dialog.modal-lg{max-width:95%;width:95%}.payment-iframe{height:650px}.modal-header{padding:1rem 1.5rem}.modal-header .modal-title{font-size:1.125rem}.cards-accepted{padding:.75rem 1.5rem}}@media only screen and (max-width: 680px){.modal-dialog{max-width:98%;width:98%;margin:1rem;max-height:calc(100vh - 2rem)}.modal-dialog.modal-lg{max-width:98%;width:98%}.payment-iframe{height:550px}.modal-header{padding:1rem}.modal-header .modal-title{font-size:1rem}.modal-header .btn-close{width:28px;height:28px;font-size:1.25rem}.cards-accepted{padding:.5rem 1rem}.cards-accepted .cards-img{height:35px}.iframe-container{padding:.5rem}}@media only screen and (max-width: 480px){.modal-dialog{margin:.5rem;max-height:calc(100vh - 1rem);border-radius:8px}.modal-content{border-radius:8px}.payment-iframe{height:500px}.cards-accepted .cards-img{height:30px}}\n"] }]
|
|
9685
10344
|
}], ctorParameters: () => [{ type: i0.Renderer2 }, { type: ConnectionService }, { type: ToastService }, { type: CoreConstantsService }, { type: ApiConstantsService }, { type: CartService }, { type: i2.ActivatedRoute }, { type: i1$4.DomSanitizer }, { type: ParametersService }], propDecorators: { paymentServiceInst: [{
|
|
9686
10345
|
type: Input
|
|
9687
10346
|
}], method: [{
|
|
@@ -11552,5 +12211,5 @@ const directives = [
|
|
|
11552
12211
|
* Generated bundle index. Do not edit.
|
|
11553
12212
|
*/
|
|
11554
12213
|
|
|
11555
|
-
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 };
|
|
12214
|
+
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 };
|
|
11556
12215
|
//# sourceMappingURL=ng-easycommerce-v18.mjs.map
|