valtech-components 2.0.498 → 2.0.500
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/esm2022/lib/services/auth/auth.service.mjs +103 -6
- package/esm2022/lib/services/auth/index.mjs +4 -1
- package/esm2022/lib/services/auth/oauth-callback.component.mjs +141 -0
- package/esm2022/lib/services/auth/oauth.service.mjs +250 -0
- package/esm2022/lib/services/auth/types.mjs +1 -1
- package/esm2022/lib/services/firebase/analytics-error-handler.mjs +141 -0
- package/esm2022/lib/services/firebase/analytics-router-tracker.mjs +99 -0
- package/esm2022/lib/services/firebase/analytics.service.mjs +597 -0
- package/esm2022/lib/services/firebase/config.mjs +21 -2
- package/esm2022/lib/services/firebase/index.mjs +6 -1
- package/fesm2022/valtech-components.mjs +1331 -7
- package/fesm2022/valtech-components.mjs.map +1 -1
- package/lib/services/auth/auth.service.d.ts +56 -3
- package/lib/services/auth/index.d.ts +2 -0
- package/lib/services/auth/oauth-callback.component.d.ts +34 -0
- package/lib/services/auth/oauth.service.d.ts +90 -0
- package/lib/services/auth/types.d.ts +69 -0
- package/lib/services/firebase/analytics-error-handler.d.ts +54 -0
- package/lib/services/firebase/analytics-router-tracker.d.ts +51 -0
- package/lib/services/firebase/analytics.service.d.ts +256 -0
- package/lib/services/firebase/index.d.ts +4 -0
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { EventEmitter, Component, Input, Output, Injectable, HostListener, inject, Pipe, ChangeDetectionStrategy, ViewChild, signal, computed, makeEnvironmentProviders, APP_INITIALIZER, ChangeDetectorRef, ElementRef,
|
|
2
|
+
import { EventEmitter, Component, Input, Output, Injectable, HostListener, inject, Pipe, ChangeDetectionStrategy, ViewChild, signal, computed, makeEnvironmentProviders, APP_INITIALIZER, ChangeDetectorRef, ElementRef, PLATFORM_ID, Inject, ErrorHandler, DestroyRef, InjectionToken, Optional, runInInjectionContext, effect } from '@angular/core';
|
|
3
3
|
import * as i2$1 from '@ionic/angular/standalone';
|
|
4
4
|
import { IonAvatar, IonCard, IonIcon, IonButton, IonSpinner, IonText, IonModal, IonHeader, IonToolbar, IonContent, IonButtons, IonTitle, IonProgressBar, IonSkeletonText, IonFab, IonFabButton, IonFabList, IonLabel, IonCardContent, IonCardHeader, IonCardTitle, IonCardSubtitle, IonCheckbox, IonTextarea, IonDatetime, IonDatetimeButton, IonInput, IonSelect, IonSelectOption, IonRadioGroup, IonRadio, IonRange, IonSearchbar, IonSegment, IonSegmentButton, IonToggle, IonAccordion, IonAccordionGroup, IonItem, IonTabBar, IonTabButton, IonBadge, IonBreadcrumb, IonBreadcrumbs, IonChip, IonPopover, IonList, IonNote, ToastController as ToastController$1, IonCol, IonRow, IonMenuButton, IonFooter, IonListHeader, IonInfiniteScroll, IonInfiniteScrollContent, IonGrid, MenuController, IonMenu, IonMenuToggle, AlertController } from '@ionic/angular/standalone';
|
|
5
5
|
import * as i1 from '@angular/common';
|
|
@@ -13,7 +13,7 @@ import * as i1$2 from '@angular/platform-browser';
|
|
|
13
13
|
import QRCodeStyling from 'qr-code-styling';
|
|
14
14
|
import * as i1$3 from '@angular/forms';
|
|
15
15
|
import { ReactiveFormsModule, FormsModule, FormControl, Validators } from '@angular/forms';
|
|
16
|
-
import { BehaviorSubject, filter, map, distinctUntilChanged, Subject, throwError, firstValueFrom, of, from } from 'rxjs';
|
|
16
|
+
import { BehaviorSubject, filter, map, distinctUntilChanged, Subject, throwError, Observable, firstValueFrom, of, from } from 'rxjs';
|
|
17
17
|
import * as i1$4 from 'ng-otp-input';
|
|
18
18
|
import { NgOtpInputComponent, NgOtpInputModule } from 'ng-otp-input';
|
|
19
19
|
import * as i2 from '@ionic/angular';
|
|
@@ -27,6 +27,7 @@ import 'prismjs/components/prism-typescript';
|
|
|
27
27
|
import 'prismjs/components/prism-bash';
|
|
28
28
|
import Swiper from 'swiper';
|
|
29
29
|
import { Navigation, Pagination, EffectFade, EffectCube, EffectCoverflow, EffectFlip, Autoplay } from 'swiper/modules';
|
|
30
|
+
import { Analytics, logEvent, setUserId, setUserProperties, provideAnalytics, getAnalytics } from '@angular/fire/analytics';
|
|
30
31
|
import { provideFirebaseApp, initializeApp } from '@angular/fire/app';
|
|
31
32
|
import * as i1$5 from '@angular/fire/auth';
|
|
32
33
|
import { provideAuth, getAuth, connectAuthEmulator, authState, signInWithCustomToken, signOut } from '@angular/fire/auth';
|
|
@@ -35,7 +36,8 @@ import { provideFirestore, getFirestore, connectFirestoreEmulator, enableIndexed
|
|
|
35
36
|
import { provideMessaging, getMessaging, Messaging, getToken, deleteToken, onMessage } from '@angular/fire/messaging';
|
|
36
37
|
import * as i1$7 from '@angular/fire/storage';
|
|
37
38
|
import { provideStorage, getStorage, connectStorageEmulator, ref, uploadBytesResumable, getDownloadURL, getMetadata, deleteObject, listAll } from '@angular/fire/storage';
|
|
38
|
-
import {
|
|
39
|
+
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
40
|
+
import { filter as filter$1, catchError, switchMap, finalize, take, tap, map as map$1 } from 'rxjs/operators';
|
|
39
41
|
import * as i1$8 from '@angular/common/http';
|
|
40
42
|
import { provideHttpClient, withInterceptors } from '@angular/common/http';
|
|
41
43
|
|
|
@@ -21481,6 +21483,828 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
21481
21483
|
* Todos los modelos de Firestore deben extender FirestoreDocument.
|
|
21482
21484
|
*/
|
|
21483
21485
|
|
|
21486
|
+
/**
|
|
21487
|
+
* Analytics Service (Firebase GA4)
|
|
21488
|
+
*
|
|
21489
|
+
* Servicio para tracking de eventos, page views y errores con Firebase Analytics.
|
|
21490
|
+
* Integra con el sistema de auth para user properties y respeta consent mode GDPR.
|
|
21491
|
+
*/
|
|
21492
|
+
/** Key por defecto para persistir consent en localStorage */
|
|
21493
|
+
const DEFAULT_CONSENT_STORAGE_KEY = 'analytics_consent';
|
|
21494
|
+
/** Máximo de eventos en historial de debug */
|
|
21495
|
+
const MAX_DEBUG_HISTORY = 100;
|
|
21496
|
+
/**
|
|
21497
|
+
* Servicio de Firebase Analytics (GA4).
|
|
21498
|
+
*
|
|
21499
|
+
* Proporciona tracking de eventos, page views, errores y métricas de performance.
|
|
21500
|
+
* Soporta GDPR Consent Mode y modo debug para desarrollo.
|
|
21501
|
+
*
|
|
21502
|
+
* @example
|
|
21503
|
+
* ```typescript
|
|
21504
|
+
* @Component({...})
|
|
21505
|
+
* export class ProductComponent {
|
|
21506
|
+
* private analytics = inject(AnalyticsService);
|
|
21507
|
+
*
|
|
21508
|
+
* onPurchase(product: Product) {
|
|
21509
|
+
* this.analytics.logEvent('purchase', {
|
|
21510
|
+
* transaction_id: order.id,
|
|
21511
|
+
* value: order.total,
|
|
21512
|
+
* currency: 'EUR'
|
|
21513
|
+
* });
|
|
21514
|
+
* }
|
|
21515
|
+
* }
|
|
21516
|
+
* ```
|
|
21517
|
+
*/
|
|
21518
|
+
class AnalyticsService {
|
|
21519
|
+
constructor(injector, config, platformId) {
|
|
21520
|
+
this.injector = injector;
|
|
21521
|
+
this.config = config;
|
|
21522
|
+
this.platformId = platformId;
|
|
21523
|
+
// ===========================================================================
|
|
21524
|
+
// ESTADO (Signals)
|
|
21525
|
+
// ===========================================================================
|
|
21526
|
+
this._consentState = signal({
|
|
21527
|
+
settings: {},
|
|
21528
|
+
updatedAt: null,
|
|
21529
|
+
hasDecided: false,
|
|
21530
|
+
});
|
|
21531
|
+
this._isDebugMode = signal(false);
|
|
21532
|
+
this._debugHistory = signal([]);
|
|
21533
|
+
/** Estado de consentimiento actual (readonly) */
|
|
21534
|
+
this.consentState = this._consentState.asReadonly();
|
|
21535
|
+
/** Indica si está en modo debug */
|
|
21536
|
+
this.isDebugMode = this._isDebugMode.asReadonly();
|
|
21537
|
+
/** Indica si analytics está habilitado y funcionando */
|
|
21538
|
+
this.isEnabled = computed(() => {
|
|
21539
|
+
return this.isAnalyticsSupported() && this._consentState().settings.analytics === true;
|
|
21540
|
+
});
|
|
21541
|
+
this.analyticsConfig = config.analyticsConfig ?? {};
|
|
21542
|
+
this.consentStorageKey =
|
|
21543
|
+
this.analyticsConfig.consentStorageKey ?? DEFAULT_CONSENT_STORAGE_KEY;
|
|
21544
|
+
this.eventPrefix = this.analyticsConfig.eventPrefix ?? '';
|
|
21545
|
+
this.samplingRate = this.analyticsConfig.samplingRate ?? 1.0;
|
|
21546
|
+
this.initializeAnalytics();
|
|
21547
|
+
}
|
|
21548
|
+
// ===========================================================================
|
|
21549
|
+
// INICIALIZACIÓN
|
|
21550
|
+
// ===========================================================================
|
|
21551
|
+
/**
|
|
21552
|
+
* Inicializa el servicio de analytics
|
|
21553
|
+
*/
|
|
21554
|
+
initializeAnalytics() {
|
|
21555
|
+
if (!isPlatformBrowser(this.platformId)) {
|
|
21556
|
+
return;
|
|
21557
|
+
}
|
|
21558
|
+
// Cargar consent desde localStorage
|
|
21559
|
+
this.loadConsentFromStorage();
|
|
21560
|
+
// Configurar debug mode
|
|
21561
|
+
const debugMode = this.analyticsConfig.debugMode ?? false;
|
|
21562
|
+
this._isDebugMode.set(debugMode);
|
|
21563
|
+
// Aplicar consent inicial a gtag
|
|
21564
|
+
this.applyConsentToGtag(this._consentState().settings);
|
|
21565
|
+
// Setear user properties por defecto
|
|
21566
|
+
if (this.analyticsConfig.defaultUserProperties) {
|
|
21567
|
+
this.setUserProperties(this.analyticsConfig.defaultUserProperties);
|
|
21568
|
+
}
|
|
21569
|
+
if (debugMode) {
|
|
21570
|
+
console.log('[Analytics] Inicializado en modo debug - eventos NO se envían a Firebase');
|
|
21571
|
+
}
|
|
21572
|
+
}
|
|
21573
|
+
/**
|
|
21574
|
+
* Obtiene la instancia de Analytics de forma perezosa.
|
|
21575
|
+
* Esto evita el error de APP_INITIALIZER de AngularFire.
|
|
21576
|
+
*/
|
|
21577
|
+
getAnalyticsInstance() {
|
|
21578
|
+
if (!this.config.enableAnalytics) {
|
|
21579
|
+
return null;
|
|
21580
|
+
}
|
|
21581
|
+
try {
|
|
21582
|
+
return this.injector.get(Analytics, null);
|
|
21583
|
+
}
|
|
21584
|
+
catch {
|
|
21585
|
+
return null;
|
|
21586
|
+
}
|
|
21587
|
+
}
|
|
21588
|
+
/**
|
|
21589
|
+
* Verifica si Analytics está soportado
|
|
21590
|
+
*/
|
|
21591
|
+
isAnalyticsSupported() {
|
|
21592
|
+
if (!isPlatformBrowser(this.platformId)) {
|
|
21593
|
+
return false;
|
|
21594
|
+
}
|
|
21595
|
+
if (!this.config.enableAnalytics) {
|
|
21596
|
+
return false;
|
|
21597
|
+
}
|
|
21598
|
+
if (!this.config.firebase.measurementId) {
|
|
21599
|
+
console.warn('[Analytics] measurementId no configurado en firebase config');
|
|
21600
|
+
return false;
|
|
21601
|
+
}
|
|
21602
|
+
return true;
|
|
21603
|
+
}
|
|
21604
|
+
/**
|
|
21605
|
+
* Verifica si debe enviar el evento (sampling)
|
|
21606
|
+
*/
|
|
21607
|
+
shouldSample() {
|
|
21608
|
+
if (this.samplingRate >= 1.0) {
|
|
21609
|
+
return true;
|
|
21610
|
+
}
|
|
21611
|
+
return Math.random() < this.samplingRate;
|
|
21612
|
+
}
|
|
21613
|
+
// ===========================================================================
|
|
21614
|
+
// PAGE VIEWS
|
|
21615
|
+
// ===========================================================================
|
|
21616
|
+
/**
|
|
21617
|
+
* Registra un page view.
|
|
21618
|
+
* Normalmente se usa automáticamente via AnalyticsRouterTracker.
|
|
21619
|
+
*
|
|
21620
|
+
* @param pagePath - Ruta de la página (ej: '/products/123')
|
|
21621
|
+
* @param pageTitle - Título de la página (opcional)
|
|
21622
|
+
*/
|
|
21623
|
+
logPageView(pagePath, pageTitle) {
|
|
21624
|
+
this.logEvent('page_view', {
|
|
21625
|
+
page_path: pagePath,
|
|
21626
|
+
page_title: pageTitle ?? document?.title,
|
|
21627
|
+
page_location: window?.location?.href,
|
|
21628
|
+
});
|
|
21629
|
+
}
|
|
21630
|
+
/**
|
|
21631
|
+
* Registra un screen view (para apps tipo SPA).
|
|
21632
|
+
*
|
|
21633
|
+
* @param screenName - Nombre del screen
|
|
21634
|
+
* @param screenClass - Clase del screen (opcional)
|
|
21635
|
+
*/
|
|
21636
|
+
logScreenView(screenName, screenClass) {
|
|
21637
|
+
this.logEvent('screen_view', {
|
|
21638
|
+
screen_name: screenName,
|
|
21639
|
+
screen_class: screenClass,
|
|
21640
|
+
});
|
|
21641
|
+
}
|
|
21642
|
+
// ===========================================================================
|
|
21643
|
+
// EVENTOS
|
|
21644
|
+
// ===========================================================================
|
|
21645
|
+
/**
|
|
21646
|
+
* Registra un evento tipado GA4.
|
|
21647
|
+
*
|
|
21648
|
+
* @param eventName - Nombre del evento (tipado)
|
|
21649
|
+
* @param params - Parámetros del evento (tipados según el nombre)
|
|
21650
|
+
*
|
|
21651
|
+
* @example
|
|
21652
|
+
* ```typescript
|
|
21653
|
+
* // Evento tipado con autocompletado
|
|
21654
|
+
* analytics.logEvent('add_to_cart', {
|
|
21655
|
+
* item_id: '123',
|
|
21656
|
+
* item_name: 'Producto',
|
|
21657
|
+
* value: 99.99,
|
|
21658
|
+
* currency: 'EUR'
|
|
21659
|
+
* });
|
|
21660
|
+
* ```
|
|
21661
|
+
*/
|
|
21662
|
+
logEvent(eventName, params) {
|
|
21663
|
+
this.trackEvent(eventName, params);
|
|
21664
|
+
}
|
|
21665
|
+
/**
|
|
21666
|
+
* Registra un evento custom con parámetros libres.
|
|
21667
|
+
* Usar cuando el evento no está en el catálogo tipado.
|
|
21668
|
+
*
|
|
21669
|
+
* @param eventName - Nombre del evento custom
|
|
21670
|
+
* @param params - Parámetros libres
|
|
21671
|
+
*/
|
|
21672
|
+
logCustomEvent(eventName, params) {
|
|
21673
|
+
const prefixedName = this.eventPrefix + eventName;
|
|
21674
|
+
this.trackEvent(prefixedName, params);
|
|
21675
|
+
}
|
|
21676
|
+
/**
|
|
21677
|
+
* Lógica común para enviar eventos
|
|
21678
|
+
*/
|
|
21679
|
+
trackEvent(eventName, params) {
|
|
21680
|
+
// Verificar consent
|
|
21681
|
+
if (!this._consentState().settings.analytics) {
|
|
21682
|
+
this.addToDebugHistory('event', eventName, params, false);
|
|
21683
|
+
return;
|
|
21684
|
+
}
|
|
21685
|
+
// Aplicar sampling
|
|
21686
|
+
if (!this.shouldSample()) {
|
|
21687
|
+
return;
|
|
21688
|
+
}
|
|
21689
|
+
// Debug mode: solo log, no enviar
|
|
21690
|
+
if (this._isDebugMode()) {
|
|
21691
|
+
this.addToDebugHistory('event', eventName, params, false);
|
|
21692
|
+
console.log(`[Analytics] Event: ${eventName}`, params);
|
|
21693
|
+
return;
|
|
21694
|
+
}
|
|
21695
|
+
// Enviar a Firebase
|
|
21696
|
+
const analytics = this.getAnalyticsInstance();
|
|
21697
|
+
if (analytics) {
|
|
21698
|
+
try {
|
|
21699
|
+
logEvent(analytics, eventName, params);
|
|
21700
|
+
this.addToDebugHistory('event', eventName, params, true);
|
|
21701
|
+
}
|
|
21702
|
+
catch (error) {
|
|
21703
|
+
console.error('[Analytics] Error enviando evento:', error);
|
|
21704
|
+
}
|
|
21705
|
+
}
|
|
21706
|
+
}
|
|
21707
|
+
// ===========================================================================
|
|
21708
|
+
// ECOMMERCE
|
|
21709
|
+
// ===========================================================================
|
|
21710
|
+
/**
|
|
21711
|
+
* Registra vista de item
|
|
21712
|
+
*/
|
|
21713
|
+
logViewItem(item) {
|
|
21714
|
+
this.logEvent('view_item', {
|
|
21715
|
+
item_id: item.item_id,
|
|
21716
|
+
item_name: item.item_name,
|
|
21717
|
+
value: item.price,
|
|
21718
|
+
currency: item.currency,
|
|
21719
|
+
});
|
|
21720
|
+
}
|
|
21721
|
+
/**
|
|
21722
|
+
* Registra agregar al carrito
|
|
21723
|
+
*/
|
|
21724
|
+
logAddToCart(item, quantity = 1) {
|
|
21725
|
+
this.logEvent('add_to_cart', {
|
|
21726
|
+
item_id: item.item_id,
|
|
21727
|
+
item_name: item.item_name,
|
|
21728
|
+
value: (item.price ?? 0) * quantity,
|
|
21729
|
+
currency: item.currency,
|
|
21730
|
+
quantity,
|
|
21731
|
+
});
|
|
21732
|
+
}
|
|
21733
|
+
/**
|
|
21734
|
+
* Registra inicio de checkout
|
|
21735
|
+
*/
|
|
21736
|
+
logBeginCheckout(items, value, currency = 'EUR') {
|
|
21737
|
+
this.logEvent('begin_checkout', {
|
|
21738
|
+
value,
|
|
21739
|
+
currency,
|
|
21740
|
+
items,
|
|
21741
|
+
});
|
|
21742
|
+
}
|
|
21743
|
+
/**
|
|
21744
|
+
* Registra compra completada
|
|
21745
|
+
*/
|
|
21746
|
+
logPurchase(transactionId, items, value, currency = 'EUR') {
|
|
21747
|
+
this.logEvent('purchase', {
|
|
21748
|
+
transaction_id: transactionId,
|
|
21749
|
+
value,
|
|
21750
|
+
currency,
|
|
21751
|
+
items,
|
|
21752
|
+
});
|
|
21753
|
+
}
|
|
21754
|
+
// ===========================================================================
|
|
21755
|
+
// USER PROPERTIES
|
|
21756
|
+
// ===========================================================================
|
|
21757
|
+
/**
|
|
21758
|
+
* Setea el userId para asociar eventos con el usuario.
|
|
21759
|
+
* Llamado automáticamente si enableAuthIntegration=true.
|
|
21760
|
+
*
|
|
21761
|
+
* @param userId - ID del usuario o null para limpiar
|
|
21762
|
+
*/
|
|
21763
|
+
setUserId(userId) {
|
|
21764
|
+
if (!this.isAnalyticsSupported()) {
|
|
21765
|
+
return;
|
|
21766
|
+
}
|
|
21767
|
+
if (this._isDebugMode()) {
|
|
21768
|
+
console.log(`[Analytics] Set userId: ${userId}`);
|
|
21769
|
+
this.addToDebugHistory('user_property', 'user_id', { userId }, false);
|
|
21770
|
+
return;
|
|
21771
|
+
}
|
|
21772
|
+
const analytics = this.getAnalyticsInstance();
|
|
21773
|
+
if (analytics) {
|
|
21774
|
+
try {
|
|
21775
|
+
setUserId(analytics, userId);
|
|
21776
|
+
}
|
|
21777
|
+
catch (error) {
|
|
21778
|
+
console.error('[Analytics] Error seteando userId:', error);
|
|
21779
|
+
}
|
|
21780
|
+
}
|
|
21781
|
+
}
|
|
21782
|
+
/**
|
|
21783
|
+
* Setea propiedades del usuario para segmentación.
|
|
21784
|
+
*
|
|
21785
|
+
* @param properties - Propiedades key-value
|
|
21786
|
+
*
|
|
21787
|
+
* @example
|
|
21788
|
+
* ```typescript
|
|
21789
|
+
* analytics.setUserProperties({
|
|
21790
|
+
* subscription_tier: 'premium',
|
|
21791
|
+
* preferred_language: 'es'
|
|
21792
|
+
* });
|
|
21793
|
+
* ```
|
|
21794
|
+
*/
|
|
21795
|
+
setUserProperties(properties) {
|
|
21796
|
+
if (!this.isAnalyticsSupported()) {
|
|
21797
|
+
return;
|
|
21798
|
+
}
|
|
21799
|
+
if (this._isDebugMode()) {
|
|
21800
|
+
console.log('[Analytics] Set user properties:', properties);
|
|
21801
|
+
this.addToDebugHistory('user_property', 'properties', properties, false);
|
|
21802
|
+
return;
|
|
21803
|
+
}
|
|
21804
|
+
const analytics = this.getAnalyticsInstance();
|
|
21805
|
+
if (analytics) {
|
|
21806
|
+
try {
|
|
21807
|
+
// Convertir a Record<string, string> para Firebase
|
|
21808
|
+
const stringProps = {};
|
|
21809
|
+
for (const [key, value] of Object.entries(properties)) {
|
|
21810
|
+
if (value !== undefined) {
|
|
21811
|
+
stringProps[key] = String(value);
|
|
21812
|
+
}
|
|
21813
|
+
}
|
|
21814
|
+
setUserProperties(analytics, stringProps);
|
|
21815
|
+
}
|
|
21816
|
+
catch (error) {
|
|
21817
|
+
console.error('[Analytics] Error seteando user properties:', error);
|
|
21818
|
+
}
|
|
21819
|
+
}
|
|
21820
|
+
}
|
|
21821
|
+
/**
|
|
21822
|
+
* Setea la organización activa (multi-tenant).
|
|
21823
|
+
* Llamado automáticamente si enableAuthIntegration=true.
|
|
21824
|
+
*
|
|
21825
|
+
* @param orgId - ID de la organización o null
|
|
21826
|
+
*/
|
|
21827
|
+
setActiveOrganization(orgId) {
|
|
21828
|
+
if (orgId) {
|
|
21829
|
+
this.setUserProperties({ active_organization: orgId });
|
|
21830
|
+
}
|
|
21831
|
+
}
|
|
21832
|
+
// ===========================================================================
|
|
21833
|
+
// ERROR TRACKING
|
|
21834
|
+
// ===========================================================================
|
|
21835
|
+
/**
|
|
21836
|
+
* Registra un error para tracking.
|
|
21837
|
+
* Integra automáticamente con Angular ErrorHandler si enableErrorTracking=true.
|
|
21838
|
+
*
|
|
21839
|
+
* @param error - Error o mensaje de error
|
|
21840
|
+
* @param context - Contexto adicional
|
|
21841
|
+
*
|
|
21842
|
+
* @example
|
|
21843
|
+
* ```typescript
|
|
21844
|
+
* try {
|
|
21845
|
+
* await riskyOperation();
|
|
21846
|
+
* } catch (error) {
|
|
21847
|
+
* analytics.logError(error, { context: 'checkout_flow' });
|
|
21848
|
+
* }
|
|
21849
|
+
* ```
|
|
21850
|
+
*/
|
|
21851
|
+
logError(error, context) {
|
|
21852
|
+
const errorMessage = error instanceof Error ? error.message : error;
|
|
21853
|
+
const errorStack = error instanceof Error ? error.stack : undefined;
|
|
21854
|
+
const errorType = error instanceof Error ? error.name : 'Error';
|
|
21855
|
+
this.logEvent('error_occurred', {
|
|
21856
|
+
error_type: errorType,
|
|
21857
|
+
error_message: errorMessage.substring(0, 100), // Limitar longitud
|
|
21858
|
+
error_stack: errorStack?.substring(0, 500),
|
|
21859
|
+
context: context ? JSON.stringify(context) : undefined,
|
|
21860
|
+
});
|
|
21861
|
+
}
|
|
21862
|
+
/**
|
|
21863
|
+
* Registra un error fatal (crash-level).
|
|
21864
|
+
*/
|
|
21865
|
+
logFatalError(error, context) {
|
|
21866
|
+
this.logError(error, { ...context, severity: 'fatal' });
|
|
21867
|
+
}
|
|
21868
|
+
// ===========================================================================
|
|
21869
|
+
// CONSENT MODE (GDPR)
|
|
21870
|
+
// ===========================================================================
|
|
21871
|
+
/**
|
|
21872
|
+
* Actualiza el estado de consentimiento del usuario.
|
|
21873
|
+
* Afecta qué datos se recolectan y envían.
|
|
21874
|
+
*
|
|
21875
|
+
* @param consent - Settings de consentimiento
|
|
21876
|
+
*
|
|
21877
|
+
* @example
|
|
21878
|
+
* ```typescript
|
|
21879
|
+
* // Usuario acepta todo
|
|
21880
|
+
* analytics.updateConsent({ analytics: true, advertising: true });
|
|
21881
|
+
*
|
|
21882
|
+
* // Usuario rechaza publicidad
|
|
21883
|
+
* analytics.updateConsent({ analytics: true, advertising: false });
|
|
21884
|
+
* ```
|
|
21885
|
+
*/
|
|
21886
|
+
updateConsent(consent) {
|
|
21887
|
+
const newState = {
|
|
21888
|
+
settings: { ...this._consentState().settings, ...consent },
|
|
21889
|
+
updatedAt: new Date(),
|
|
21890
|
+
hasDecided: true,
|
|
21891
|
+
};
|
|
21892
|
+
this._consentState.set(newState);
|
|
21893
|
+
this.saveConsentToStorage(newState);
|
|
21894
|
+
this.applyConsentToGtag(newState.settings);
|
|
21895
|
+
this.addToDebugHistory('consent', 'update', consent, false);
|
|
21896
|
+
}
|
|
21897
|
+
/**
|
|
21898
|
+
* Deniega todo consentimiento.
|
|
21899
|
+
*/
|
|
21900
|
+
denyAllConsent() {
|
|
21901
|
+
this.updateConsent({
|
|
21902
|
+
analytics: false,
|
|
21903
|
+
advertising: false,
|
|
21904
|
+
functionality: false,
|
|
21905
|
+
security: true, // Siempre permitido
|
|
21906
|
+
});
|
|
21907
|
+
}
|
|
21908
|
+
/**
|
|
21909
|
+
* Acepta todo consentimiento.
|
|
21910
|
+
*/
|
|
21911
|
+
grantAllConsent() {
|
|
21912
|
+
this.updateConsent({
|
|
21913
|
+
analytics: true,
|
|
21914
|
+
advertising: true,
|
|
21915
|
+
functionality: true,
|
|
21916
|
+
security: true,
|
|
21917
|
+
});
|
|
21918
|
+
}
|
|
21919
|
+
/**
|
|
21920
|
+
* Obtiene el estado actual de consentimiento.
|
|
21921
|
+
*/
|
|
21922
|
+
getConsentState() {
|
|
21923
|
+
return this._consentState();
|
|
21924
|
+
}
|
|
21925
|
+
/**
|
|
21926
|
+
* Aplica consent settings a gtag (GA4 Consent Mode v2)
|
|
21927
|
+
*/
|
|
21928
|
+
applyConsentToGtag(settings) {
|
|
21929
|
+
if (!isPlatformBrowser(this.platformId)) {
|
|
21930
|
+
return;
|
|
21931
|
+
}
|
|
21932
|
+
const gtag = window.gtag;
|
|
21933
|
+
if (typeof gtag !== 'function') {
|
|
21934
|
+
return;
|
|
21935
|
+
}
|
|
21936
|
+
try {
|
|
21937
|
+
gtag('consent', 'update', {
|
|
21938
|
+
analytics_storage: settings.analytics ? 'granted' : 'denied',
|
|
21939
|
+
ad_storage: settings.advertising ? 'granted' : 'denied',
|
|
21940
|
+
ad_user_data: settings.advertising ? 'granted' : 'denied',
|
|
21941
|
+
ad_personalization: settings.advertising ? 'granted' : 'denied',
|
|
21942
|
+
functionality_storage: settings.functionality ? 'granted' : 'denied',
|
|
21943
|
+
security_storage: settings.security !== false ? 'granted' : 'denied',
|
|
21944
|
+
});
|
|
21945
|
+
}
|
|
21946
|
+
catch (error) {
|
|
21947
|
+
console.warn('[Analytics] Error aplicando consent a gtag:', error);
|
|
21948
|
+
}
|
|
21949
|
+
}
|
|
21950
|
+
/**
|
|
21951
|
+
* Carga consent desde localStorage
|
|
21952
|
+
*/
|
|
21953
|
+
loadConsentFromStorage() {
|
|
21954
|
+
if (!isPlatformBrowser(this.platformId)) {
|
|
21955
|
+
return;
|
|
21956
|
+
}
|
|
21957
|
+
try {
|
|
21958
|
+
const stored = localStorage.getItem(this.consentStorageKey);
|
|
21959
|
+
if (stored) {
|
|
21960
|
+
const parsed = JSON.parse(stored);
|
|
21961
|
+
this._consentState.set({
|
|
21962
|
+
...parsed,
|
|
21963
|
+
updatedAt: parsed.updatedAt ? new Date(parsed.updatedAt) : null,
|
|
21964
|
+
});
|
|
21965
|
+
}
|
|
21966
|
+
else if (this.analyticsConfig.defaultConsent) {
|
|
21967
|
+
// Aplicar consent por defecto si no hay guardado
|
|
21968
|
+
this._consentState.set({
|
|
21969
|
+
settings: this.analyticsConfig.defaultConsent,
|
|
21970
|
+
updatedAt: null,
|
|
21971
|
+
hasDecided: false,
|
|
21972
|
+
});
|
|
21973
|
+
}
|
|
21974
|
+
}
|
|
21975
|
+
catch (error) {
|
|
21976
|
+
console.warn('[Analytics] Error cargando consent:', error);
|
|
21977
|
+
}
|
|
21978
|
+
}
|
|
21979
|
+
/**
|
|
21980
|
+
* Guarda consent en localStorage
|
|
21981
|
+
*/
|
|
21982
|
+
saveConsentToStorage(state) {
|
|
21983
|
+
if (!isPlatformBrowser(this.platformId)) {
|
|
21984
|
+
return;
|
|
21985
|
+
}
|
|
21986
|
+
try {
|
|
21987
|
+
localStorage.setItem(this.consentStorageKey, JSON.stringify(state));
|
|
21988
|
+
}
|
|
21989
|
+
catch (error) {
|
|
21990
|
+
console.warn('[Analytics] Error guardando consent:', error);
|
|
21991
|
+
}
|
|
21992
|
+
}
|
|
21993
|
+
// ===========================================================================
|
|
21994
|
+
// TIMING / PERFORMANCE
|
|
21995
|
+
// ===========================================================================
|
|
21996
|
+
/**
|
|
21997
|
+
* Registra una métrica de timing/performance.
|
|
21998
|
+
*
|
|
21999
|
+
* @param name - Nombre de la métrica
|
|
22000
|
+
* @param valueMs - Valor en milisegundos
|
|
22001
|
+
* @param params - Parámetros adicionales
|
|
22002
|
+
*
|
|
22003
|
+
* @example
|
|
22004
|
+
* ```typescript
|
|
22005
|
+
* const start = performance.now();
|
|
22006
|
+
* await loadData();
|
|
22007
|
+
* analytics.logTiming('data_load', performance.now() - start, {
|
|
22008
|
+
* category: 'api',
|
|
22009
|
+
* endpoint: '/products'
|
|
22010
|
+
* });
|
|
22011
|
+
* ```
|
|
22012
|
+
*/
|
|
22013
|
+
logTiming(name, valueMs, params) {
|
|
22014
|
+
this.logEvent('performance_metric', {
|
|
22015
|
+
metric_name: name,
|
|
22016
|
+
value: Math.round(valueMs),
|
|
22017
|
+
unit: 'ms',
|
|
22018
|
+
...params,
|
|
22019
|
+
});
|
|
22020
|
+
}
|
|
22021
|
+
// ===========================================================================
|
|
22022
|
+
// DEBUG
|
|
22023
|
+
// ===========================================================================
|
|
22024
|
+
/**
|
|
22025
|
+
* Habilita/deshabilita modo debug.
|
|
22026
|
+
* En debug: logea a consola, no envía a Firebase.
|
|
22027
|
+
*/
|
|
22028
|
+
setDebugMode(enabled) {
|
|
22029
|
+
this._isDebugMode.set(enabled);
|
|
22030
|
+
console.log(`[Analytics] Debug mode: ${enabled ? 'ON' : 'OFF'}`);
|
|
22031
|
+
}
|
|
22032
|
+
/**
|
|
22033
|
+
* Obtiene historial de eventos (solo en debug mode).
|
|
22034
|
+
* Útil para testing y desarrollo.
|
|
22035
|
+
*/
|
|
22036
|
+
getDebugHistory() {
|
|
22037
|
+
return this._debugHistory();
|
|
22038
|
+
}
|
|
22039
|
+
/**
|
|
22040
|
+
* Limpia historial de debug.
|
|
22041
|
+
*/
|
|
22042
|
+
clearDebugHistory() {
|
|
22043
|
+
this._debugHistory.set([]);
|
|
22044
|
+
}
|
|
22045
|
+
/**
|
|
22046
|
+
* Agrega evento al historial de debug
|
|
22047
|
+
*/
|
|
22048
|
+
addToDebugHistory(type, name, params, sent = false) {
|
|
22049
|
+
if (!this._isDebugMode()) {
|
|
22050
|
+
return;
|
|
22051
|
+
}
|
|
22052
|
+
const event = {
|
|
22053
|
+
timestamp: new Date(),
|
|
22054
|
+
type,
|
|
22055
|
+
name,
|
|
22056
|
+
params,
|
|
22057
|
+
sent,
|
|
22058
|
+
};
|
|
22059
|
+
this._debugHistory.update((history) => {
|
|
22060
|
+
const newHistory = [event, ...history];
|
|
22061
|
+
return newHistory.slice(0, MAX_DEBUG_HISTORY);
|
|
22062
|
+
});
|
|
22063
|
+
}
|
|
22064
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AnalyticsService, deps: [{ token: i0.Injector }, { token: VALTECH_FIREBASE_CONFIG }, { token: PLATFORM_ID }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
22065
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AnalyticsService, providedIn: 'root' }); }
|
|
22066
|
+
}
|
|
22067
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AnalyticsService, decorators: [{
|
|
22068
|
+
type: Injectable,
|
|
22069
|
+
args: [{ providedIn: 'root' }]
|
|
22070
|
+
}], ctorParameters: () => [{ type: i0.Injector }, { type: undefined, decorators: [{
|
|
22071
|
+
type: Inject,
|
|
22072
|
+
args: [VALTECH_FIREBASE_CONFIG]
|
|
22073
|
+
}] }, { type: Object, decorators: [{
|
|
22074
|
+
type: Inject,
|
|
22075
|
+
args: [PLATFORM_ID]
|
|
22076
|
+
}] }] });
|
|
22077
|
+
|
|
22078
|
+
/**
|
|
22079
|
+
* Analytics Error Handler
|
|
22080
|
+
*
|
|
22081
|
+
* ErrorHandler personalizado que envía errores no capturados a Firebase Analytics.
|
|
22082
|
+
* Se activa si enableErrorTracking=true en analyticsConfig.
|
|
22083
|
+
*/
|
|
22084
|
+
/**
|
|
22085
|
+
* ErrorHandler que trackea errores en Firebase Analytics.
|
|
22086
|
+
*
|
|
22087
|
+
* Captura errores no manejados de la aplicación y los envía a GA4
|
|
22088
|
+
* como eventos 'error_occurred'. También delega al ErrorHandler
|
|
22089
|
+
* default para mantener el comportamiento de console.error.
|
|
22090
|
+
*
|
|
22091
|
+
* @example
|
|
22092
|
+
* ```typescript
|
|
22093
|
+
* // Se activa automáticamente si enableErrorTracking=true
|
|
22094
|
+
* provideValtechFirebase({
|
|
22095
|
+
* firebase: environment.firebase,
|
|
22096
|
+
* enableAnalytics: true,
|
|
22097
|
+
* analyticsConfig: {
|
|
22098
|
+
* enableErrorTracking: true,
|
|
22099
|
+
* },
|
|
22100
|
+
* });
|
|
22101
|
+
* ```
|
|
22102
|
+
*/
|
|
22103
|
+
class AnalyticsErrorHandler {
|
|
22104
|
+
constructor() {
|
|
22105
|
+
this.analytics = inject(AnalyticsService);
|
|
22106
|
+
this.defaultHandler = new ErrorHandler();
|
|
22107
|
+
}
|
|
22108
|
+
/**
|
|
22109
|
+
* Maneja un error no capturado.
|
|
22110
|
+
* Envía el error a Analytics y luego al handler default.
|
|
22111
|
+
*/
|
|
22112
|
+
handleError(error) {
|
|
22113
|
+
// Enviar a Analytics
|
|
22114
|
+
try {
|
|
22115
|
+
this.trackError(error);
|
|
22116
|
+
}
|
|
22117
|
+
catch (trackingError) {
|
|
22118
|
+
// No fallar si el tracking falla
|
|
22119
|
+
console.warn('[AnalyticsErrorHandler] Error tracking failed:', trackingError);
|
|
22120
|
+
}
|
|
22121
|
+
// Delegar al handler default (console.error)
|
|
22122
|
+
this.defaultHandler.handleError(error);
|
|
22123
|
+
}
|
|
22124
|
+
/**
|
|
22125
|
+
* Trackea el error en Analytics
|
|
22126
|
+
*/
|
|
22127
|
+
trackError(error) {
|
|
22128
|
+
// Extraer información del error
|
|
22129
|
+
const errorInfo = this.extractErrorInfo(error);
|
|
22130
|
+
this.analytics.logError(errorInfo.error, {
|
|
22131
|
+
source: 'uncaught',
|
|
22132
|
+
url: this.getCurrentUrl(),
|
|
22133
|
+
...errorInfo.context,
|
|
22134
|
+
});
|
|
22135
|
+
}
|
|
22136
|
+
/**
|
|
22137
|
+
* Extrae información útil del error
|
|
22138
|
+
*/
|
|
22139
|
+
extractErrorInfo(error) {
|
|
22140
|
+
const context = {};
|
|
22141
|
+
// Error estándar
|
|
22142
|
+
if (error instanceof Error) {
|
|
22143
|
+
// Detectar errores de chunk loading (lazy loading)
|
|
22144
|
+
if (error.message.includes('Loading chunk')) {
|
|
22145
|
+
context['error_category'] = 'chunk_loading';
|
|
22146
|
+
}
|
|
22147
|
+
// Detectar errores de red
|
|
22148
|
+
if (error.message.includes('NetworkError') || error.message.includes('Failed to fetch')) {
|
|
22149
|
+
context['error_category'] = 'network';
|
|
22150
|
+
}
|
|
22151
|
+
return { error, context };
|
|
22152
|
+
}
|
|
22153
|
+
// ErrorEvent (ej: errores de script)
|
|
22154
|
+
if (typeof ErrorEvent !== 'undefined' && error instanceof ErrorEvent) {
|
|
22155
|
+
return {
|
|
22156
|
+
error: new Error(error.message || 'Script error'),
|
|
22157
|
+
context: {
|
|
22158
|
+
filename: error.filename || 'unknown',
|
|
22159
|
+
lineno: String(error.lineno || 0),
|
|
22160
|
+
colno: String(error.colno || 0),
|
|
22161
|
+
},
|
|
22162
|
+
};
|
|
22163
|
+
}
|
|
22164
|
+
// PromiseRejection
|
|
22165
|
+
if (this.isPromiseRejection(error)) {
|
|
22166
|
+
const reason = error.reason;
|
|
22167
|
+
if (reason instanceof Error) {
|
|
22168
|
+
return {
|
|
22169
|
+
error: reason,
|
|
22170
|
+
context: { error_category: 'unhandled_promise' },
|
|
22171
|
+
};
|
|
22172
|
+
}
|
|
22173
|
+
return {
|
|
22174
|
+
error: new Error(String(reason) || 'Unhandled promise rejection'),
|
|
22175
|
+
context: { error_category: 'unhandled_promise' },
|
|
22176
|
+
};
|
|
22177
|
+
}
|
|
22178
|
+
// Objeto con message
|
|
22179
|
+
if (error && typeof error === 'object' && 'message' in error) {
|
|
22180
|
+
return {
|
|
22181
|
+
error: new Error(String(error.message)),
|
|
22182
|
+
context,
|
|
22183
|
+
};
|
|
22184
|
+
}
|
|
22185
|
+
// Fallback: convertir a string
|
|
22186
|
+
return {
|
|
22187
|
+
error: new Error(String(error) || 'Unknown error'),
|
|
22188
|
+
context,
|
|
22189
|
+
};
|
|
22190
|
+
}
|
|
22191
|
+
/**
|
|
22192
|
+
* Verifica si es un PromiseRejectionEvent
|
|
22193
|
+
*/
|
|
22194
|
+
isPromiseRejection(error) {
|
|
22195
|
+
return (typeof PromiseRejectionEvent !== 'undefined' &&
|
|
22196
|
+
error instanceof PromiseRejectionEvent);
|
|
22197
|
+
}
|
|
22198
|
+
/**
|
|
22199
|
+
* Obtiene la URL actual de forma segura
|
|
22200
|
+
*/
|
|
22201
|
+
getCurrentUrl() {
|
|
22202
|
+
try {
|
|
22203
|
+
return window?.location?.href || 'unknown';
|
|
22204
|
+
}
|
|
22205
|
+
catch {
|
|
22206
|
+
return 'unknown';
|
|
22207
|
+
}
|
|
22208
|
+
}
|
|
22209
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AnalyticsErrorHandler, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
22210
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AnalyticsErrorHandler }); }
|
|
22211
|
+
}
|
|
22212
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AnalyticsErrorHandler, decorators: [{
|
|
22213
|
+
type: Injectable
|
|
22214
|
+
}] });
|
|
22215
|
+
|
|
22216
|
+
/**
|
|
22217
|
+
* Analytics Router Tracker
|
|
22218
|
+
*
|
|
22219
|
+
* Servicio que trackea automáticamente page views cuando el usuario navega.
|
|
22220
|
+
* Se activa automáticamente si enablePageViewTracking=true en analyticsConfig.
|
|
22221
|
+
*/
|
|
22222
|
+
/**
|
|
22223
|
+
* Tracker automático de page views via Router.
|
|
22224
|
+
*
|
|
22225
|
+
* Este servicio escucha eventos de navegación del Router y registra
|
|
22226
|
+
* page views automáticamente en Firebase Analytics.
|
|
22227
|
+
*
|
|
22228
|
+
* Se excluyen rutas configuradas en `analyticsConfig.excludeRoutes`.
|
|
22229
|
+
*
|
|
22230
|
+
* @example
|
|
22231
|
+
* ```typescript
|
|
22232
|
+
* // Se activa automáticamente si enablePageViewTracking=true
|
|
22233
|
+
* provideValtechFirebase({
|
|
22234
|
+
* firebase: environment.firebase,
|
|
22235
|
+
* enableAnalytics: true,
|
|
22236
|
+
* analyticsConfig: {
|
|
22237
|
+
* enablePageViewTracking: true,
|
|
22238
|
+
* excludeRoutes: ['/admin/*', '/debug/*'],
|
|
22239
|
+
* },
|
|
22240
|
+
* });
|
|
22241
|
+
* ```
|
|
22242
|
+
*/
|
|
22243
|
+
class AnalyticsRouterTracker {
|
|
22244
|
+
constructor(config) {
|
|
22245
|
+
this.config = config;
|
|
22246
|
+
this.analytics = inject(AnalyticsService);
|
|
22247
|
+
this.router = inject(Router);
|
|
22248
|
+
this.destroyRef = inject(DestroyRef);
|
|
22249
|
+
const analyticsConfig = config.analyticsConfig ?? {};
|
|
22250
|
+
this.enabled = analyticsConfig.enablePageViewTracking !== false;
|
|
22251
|
+
this.excludePatterns = this.compileExcludePatterns(analyticsConfig.excludeRoutes ?? []);
|
|
22252
|
+
if (this.enabled && config.enableAnalytics) {
|
|
22253
|
+
this.startTracking();
|
|
22254
|
+
}
|
|
22255
|
+
}
|
|
22256
|
+
/**
|
|
22257
|
+
* Inicia el tracking de navegación
|
|
22258
|
+
*/
|
|
22259
|
+
startTracking() {
|
|
22260
|
+
this.router.events
|
|
22261
|
+
.pipe(filter$1((event) => event instanceof NavigationEnd), filter$1((event) => !this.isExcluded(event.urlAfterRedirects)), takeUntilDestroyed(this.destroyRef))
|
|
22262
|
+
.subscribe((event) => {
|
|
22263
|
+
this.analytics.logPageView(event.urlAfterRedirects);
|
|
22264
|
+
});
|
|
22265
|
+
}
|
|
22266
|
+
/**
|
|
22267
|
+
* Compila patrones de exclusión a RegExp
|
|
22268
|
+
*/
|
|
22269
|
+
compileExcludePatterns(patterns) {
|
|
22270
|
+
return patterns.map((pattern) => {
|
|
22271
|
+
// Convertir glob pattern a regex
|
|
22272
|
+
// Ej: '/admin/*' -> /^\/admin\/.*$/
|
|
22273
|
+
const regexPattern = pattern
|
|
22274
|
+
.replace(/[.+?^${}()|[\]\\]/g, '\\$&') // Escapar caracteres especiales
|
|
22275
|
+
.replace(/\*/g, '.*'); // Convertir * a .*
|
|
22276
|
+
return new RegExp(`^${regexPattern}$`);
|
|
22277
|
+
});
|
|
22278
|
+
}
|
|
22279
|
+
/**
|
|
22280
|
+
* Verifica si una URL debe ser excluida del tracking
|
|
22281
|
+
*/
|
|
22282
|
+
isExcluded(url) {
|
|
22283
|
+
// Remover query params para la comparación
|
|
22284
|
+
const path = url.split('?')[0];
|
|
22285
|
+
return this.excludePatterns.some((pattern) => pattern.test(path));
|
|
22286
|
+
}
|
|
22287
|
+
/**
|
|
22288
|
+
* Registra un page view manualmente.
|
|
22289
|
+
* Útil para casos donde necesitas trackear manualmente.
|
|
22290
|
+
*/
|
|
22291
|
+
trackPageView(path, title) {
|
|
22292
|
+
if (this.isExcluded(path)) {
|
|
22293
|
+
return;
|
|
22294
|
+
}
|
|
22295
|
+
this.analytics.logPageView(path, title);
|
|
22296
|
+
}
|
|
22297
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AnalyticsRouterTracker, deps: [{ token: VALTECH_FIREBASE_CONFIG }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
22298
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AnalyticsRouterTracker, providedIn: 'root' }); }
|
|
22299
|
+
}
|
|
22300
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AnalyticsRouterTracker, decorators: [{
|
|
22301
|
+
type: Injectable,
|
|
22302
|
+
args: [{ providedIn: 'root' }]
|
|
22303
|
+
}], ctorParameters: () => [{ type: undefined, decorators: [{
|
|
22304
|
+
type: Inject,
|
|
22305
|
+
args: [VALTECH_FIREBASE_CONFIG]
|
|
22306
|
+
}] }] });
|
|
22307
|
+
|
|
21484
22308
|
/**
|
|
21485
22309
|
* Firebase Configuration
|
|
21486
22310
|
*
|
|
@@ -21575,6 +22399,22 @@ function provideValtechFirebase(config) {
|
|
|
21575
22399
|
}
|
|
21576
22400
|
providers.push(provideMessaging(() => getMessaging()));
|
|
21577
22401
|
}
|
|
22402
|
+
// Analytics (GA4) - solo si está explícitamente habilitado
|
|
22403
|
+
// Requiere measurementId en firebase config
|
|
22404
|
+
if (config.enableAnalytics && config.firebase.measurementId) {
|
|
22405
|
+
providers.push(provideAnalytics(() => getAnalytics()));
|
|
22406
|
+
// Router tracker para page views automáticos (por defecto habilitado)
|
|
22407
|
+
if (config.analyticsConfig?.enablePageViewTracking !== false) {
|
|
22408
|
+
providers.push(AnalyticsRouterTracker);
|
|
22409
|
+
}
|
|
22410
|
+
// Error handler para tracking automático de errores
|
|
22411
|
+
if (config.analyticsConfig?.enableErrorTracking) {
|
|
22412
|
+
providers.push({
|
|
22413
|
+
provide: ErrorHandler,
|
|
22414
|
+
useClass: AnalyticsErrorHandler,
|
|
22415
|
+
});
|
|
22416
|
+
}
|
|
22417
|
+
}
|
|
21578
22418
|
return makeEnvironmentProviders(providers);
|
|
21579
22419
|
}
|
|
21580
22420
|
/**
|
|
@@ -25255,6 +26095,250 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
25255
26095
|
args: [VALTECH_AUTH_CONFIG]
|
|
25256
26096
|
}] }] });
|
|
25257
26097
|
|
|
26098
|
+
/**
|
|
26099
|
+
* Servicio de OAuth para login social.
|
|
26100
|
+
*
|
|
26101
|
+
* Implementa flujo OAuth server-side con popup:
|
|
26102
|
+
* 1. Frontend abre popup hacia backend
|
|
26103
|
+
* 2. Backend redirige a provider (Google, Apple, Microsoft)
|
|
26104
|
+
* 3. Usuario autoriza
|
|
26105
|
+
* 4. Backend intercambia code, genera JWT, redirige con tokens
|
|
26106
|
+
* 5. Popup envía tokens a ventana padre via postMessage
|
|
26107
|
+
*
|
|
26108
|
+
* @example
|
|
26109
|
+
* ```typescript
|
|
26110
|
+
* import { OAuthService, AuthService } from 'valtech-components';
|
|
26111
|
+
*
|
|
26112
|
+
* @Component({...})
|
|
26113
|
+
* export class LoginComponent {
|
|
26114
|
+
* private oauth = inject(OAuthService);
|
|
26115
|
+
* private auth = inject(AuthService);
|
|
26116
|
+
*
|
|
26117
|
+
* async loginWithGoogle() {
|
|
26118
|
+
* this.oauth.startFlow('google').subscribe({
|
|
26119
|
+
* next: (result) => {
|
|
26120
|
+
* // Tokens recibidos, guardar en auth state
|
|
26121
|
+
* this.auth.handleOAuthSuccess(result);
|
|
26122
|
+
* this.router.navigate(['/']);
|
|
26123
|
+
* },
|
|
26124
|
+
* error: (error) => {
|
|
26125
|
+
* console.error('OAuth failed:', error);
|
|
26126
|
+
* }
|
|
26127
|
+
* });
|
|
26128
|
+
* }
|
|
26129
|
+
* }
|
|
26130
|
+
* ```
|
|
26131
|
+
*/
|
|
26132
|
+
class OAuthService {
|
|
26133
|
+
constructor(config, http, ngZone) {
|
|
26134
|
+
this.config = config;
|
|
26135
|
+
this.http = http;
|
|
26136
|
+
this.ngZone = ngZone;
|
|
26137
|
+
this.popup = null;
|
|
26138
|
+
this.messageHandler = null;
|
|
26139
|
+
this.checkClosedInterval = null;
|
|
26140
|
+
}
|
|
26141
|
+
/**
|
|
26142
|
+
* Inicia flujo OAuth en popup.
|
|
26143
|
+
* Retorna Observable que emite cuando el usuario completa el flujo.
|
|
26144
|
+
*
|
|
26145
|
+
* @param provider - Proveedor OAuth ('google', 'apple', 'microsoft')
|
|
26146
|
+
* @returns Observable que emite OAuthResult o error
|
|
26147
|
+
*/
|
|
26148
|
+
startFlow(provider) {
|
|
26149
|
+
return new Observable(observer => {
|
|
26150
|
+
// Construir URL de inicio
|
|
26151
|
+
const redirectUri = `${window.location.origin}/auth/oauth/callback`;
|
|
26152
|
+
const startUrl = `${this.config.apiUrl}/v2/auth/oauth/${provider}/start?redirect_uri=${encodeURIComponent(redirectUri)}`;
|
|
26153
|
+
// Abrir popup centrado
|
|
26154
|
+
const width = 500;
|
|
26155
|
+
const height = 600;
|
|
26156
|
+
const left = window.screenX + (window.outerWidth - width) / 2;
|
|
26157
|
+
const top = window.screenY + (window.outerHeight - height) / 2;
|
|
26158
|
+
const features = `width=${width},height=${height},left=${left},top=${top},popup=yes`;
|
|
26159
|
+
this.popup = window.open(startUrl, 'oauth', features);
|
|
26160
|
+
if (!this.popup) {
|
|
26161
|
+
observer.error({
|
|
26162
|
+
code: 'POPUP_BLOCKED',
|
|
26163
|
+
message: 'El navegador bloqueó la ventana emergente. Por favor, permite popups para este sitio.',
|
|
26164
|
+
});
|
|
26165
|
+
return () => { };
|
|
26166
|
+
}
|
|
26167
|
+
// Escuchar mensajes del popup
|
|
26168
|
+
this.messageHandler = (event) => {
|
|
26169
|
+
// Validar origen
|
|
26170
|
+
if (event.origin !== window.location.origin) {
|
|
26171
|
+
return;
|
|
26172
|
+
}
|
|
26173
|
+
// Validar tipo de mensaje
|
|
26174
|
+
const data = event.data;
|
|
26175
|
+
if (data?.type !== 'oauth-callback') {
|
|
26176
|
+
return;
|
|
26177
|
+
}
|
|
26178
|
+
// Limpiar
|
|
26179
|
+
this.cleanup();
|
|
26180
|
+
// Emitir resultado dentro de NgZone para trigger change detection
|
|
26181
|
+
this.ngZone.run(() => {
|
|
26182
|
+
if (data.error) {
|
|
26183
|
+
observer.error(data.error);
|
|
26184
|
+
}
|
|
26185
|
+
else if (data.tokens) {
|
|
26186
|
+
observer.next(data.tokens);
|
|
26187
|
+
observer.complete();
|
|
26188
|
+
}
|
|
26189
|
+
else {
|
|
26190
|
+
observer.error({
|
|
26191
|
+
code: 'INVALID_RESPONSE',
|
|
26192
|
+
message: 'Respuesta inválida del servidor de autenticación',
|
|
26193
|
+
});
|
|
26194
|
+
}
|
|
26195
|
+
});
|
|
26196
|
+
};
|
|
26197
|
+
window.addEventListener('message', this.messageHandler);
|
|
26198
|
+
// Verificar si popup se cierra manualmente
|
|
26199
|
+
this.checkClosedInterval = setInterval(() => {
|
|
26200
|
+
if (this.popup?.closed) {
|
|
26201
|
+
this.cleanup();
|
|
26202
|
+
this.ngZone.run(() => {
|
|
26203
|
+
observer.error({
|
|
26204
|
+
code: 'POPUP_CLOSED',
|
|
26205
|
+
message: 'Se cerró la ventana de autenticación',
|
|
26206
|
+
});
|
|
26207
|
+
});
|
|
26208
|
+
}
|
|
26209
|
+
}, 500);
|
|
26210
|
+
// Cleanup cuando el observable se destruye
|
|
26211
|
+
return () => this.cleanup();
|
|
26212
|
+
});
|
|
26213
|
+
}
|
|
26214
|
+
/**
|
|
26215
|
+
* Inicia flujo de linking para vincular un proveedor adicional.
|
|
26216
|
+
* Requiere que el usuario esté autenticado.
|
|
26217
|
+
*
|
|
26218
|
+
* @param provider - Proveedor OAuth a vincular
|
|
26219
|
+
* @returns Observable que emite cuando se completa el linking
|
|
26220
|
+
*/
|
|
26221
|
+
startLinkFlow(provider) {
|
|
26222
|
+
return new Observable(observer => {
|
|
26223
|
+
const redirectUri = `${window.location.origin}/auth/oauth/callback`;
|
|
26224
|
+
const startUrl = `${this.config.apiUrl}/v2/auth/oauth/link/${provider}/start?redirect_uri=${encodeURIComponent(redirectUri)}`;
|
|
26225
|
+
const width = 500;
|
|
26226
|
+
const height = 600;
|
|
26227
|
+
const left = window.screenX + (window.outerWidth - width) / 2;
|
|
26228
|
+
const top = window.screenY + (window.outerHeight - height) / 2;
|
|
26229
|
+
const features = `width=${width},height=${height},left=${left},top=${top},popup=yes`;
|
|
26230
|
+
this.popup = window.open(startUrl, 'oauth-link', features);
|
|
26231
|
+
if (!this.popup) {
|
|
26232
|
+
observer.error({
|
|
26233
|
+
code: 'POPUP_BLOCKED',
|
|
26234
|
+
message: 'El navegador bloqueó la ventana emergente',
|
|
26235
|
+
});
|
|
26236
|
+
return () => { }; // cleanup function
|
|
26237
|
+
}
|
|
26238
|
+
this.messageHandler = (event) => {
|
|
26239
|
+
if (event.origin !== window.location.origin)
|
|
26240
|
+
return;
|
|
26241
|
+
const data = event.data;
|
|
26242
|
+
if (data?.type !== 'oauth-callback')
|
|
26243
|
+
return;
|
|
26244
|
+
this.cleanup();
|
|
26245
|
+
this.ngZone.run(() => {
|
|
26246
|
+
if (data.error) {
|
|
26247
|
+
observer.error(data.error);
|
|
26248
|
+
}
|
|
26249
|
+
else {
|
|
26250
|
+
observer.next(data.tokens || {});
|
|
26251
|
+
observer.complete();
|
|
26252
|
+
}
|
|
26253
|
+
});
|
|
26254
|
+
};
|
|
26255
|
+
window.addEventListener('message', this.messageHandler);
|
|
26256
|
+
this.checkClosedInterval = setInterval(() => {
|
|
26257
|
+
if (this.popup?.closed) {
|
|
26258
|
+
this.cleanup();
|
|
26259
|
+
this.ngZone.run(() => {
|
|
26260
|
+
observer.error({
|
|
26261
|
+
code: 'POPUP_CLOSED',
|
|
26262
|
+
message: 'Se cerró la ventana de autenticación',
|
|
26263
|
+
});
|
|
26264
|
+
});
|
|
26265
|
+
}
|
|
26266
|
+
}, 500);
|
|
26267
|
+
return () => this.cleanup();
|
|
26268
|
+
});
|
|
26269
|
+
}
|
|
26270
|
+
/**
|
|
26271
|
+
* Obtiene los proveedores OAuth vinculados al usuario.
|
|
26272
|
+
*/
|
|
26273
|
+
getLinkedProviders() {
|
|
26274
|
+
return this.http
|
|
26275
|
+
.get(`${this.config.apiUrl}/v2/auth/oauth/providers`)
|
|
26276
|
+
.pipe(catchError(error => throwError(() => ({
|
|
26277
|
+
code: error.error?.code || 'FETCH_ERROR',
|
|
26278
|
+
message: error.error?.message || 'Error al obtener proveedores vinculados',
|
|
26279
|
+
}))));
|
|
26280
|
+
}
|
|
26281
|
+
/**
|
|
26282
|
+
* Desvincula un proveedor OAuth.
|
|
26283
|
+
*/
|
|
26284
|
+
unlinkProvider(provider) {
|
|
26285
|
+
return this.http
|
|
26286
|
+
.post(`${this.config.apiUrl}/v2/auth/oauth/unlink`, { provider })
|
|
26287
|
+
.pipe(catchError(error => throwError(() => ({
|
|
26288
|
+
code: error.error?.code || 'UNLINK_ERROR',
|
|
26289
|
+
message: error.error?.message || 'Error al desvincular proveedor',
|
|
26290
|
+
}))));
|
|
26291
|
+
}
|
|
26292
|
+
/**
|
|
26293
|
+
* Establece contraseña para usuarios que solo tienen OAuth.
|
|
26294
|
+
*/
|
|
26295
|
+
setPassword(password) {
|
|
26296
|
+
return this.http
|
|
26297
|
+
.post(`${this.config.apiUrl}/v2/auth/oauth/set-password`, { password })
|
|
26298
|
+
.pipe(catchError(error => throwError(() => ({
|
|
26299
|
+
code: error.error?.code || 'SET_PASSWORD_ERROR',
|
|
26300
|
+
message: error.error?.message || 'Error al establecer contraseña',
|
|
26301
|
+
}))));
|
|
26302
|
+
}
|
|
26303
|
+
/**
|
|
26304
|
+
* Verifica si el usuario tiene contraseña establecida.
|
|
26305
|
+
*/
|
|
26306
|
+
hasPassword() {
|
|
26307
|
+
return this.http
|
|
26308
|
+
.get(`${this.config.apiUrl}/v2/auth/oauth/has-password`)
|
|
26309
|
+
.pipe(catchError(error => throwError(() => ({
|
|
26310
|
+
code: error.error?.code || 'CHECK_PASSWORD_ERROR',
|
|
26311
|
+
message: error.error?.message || 'Error al verificar contraseña',
|
|
26312
|
+
}))));
|
|
26313
|
+
}
|
|
26314
|
+
/**
|
|
26315
|
+
* Limpia recursos del popup.
|
|
26316
|
+
*/
|
|
26317
|
+
cleanup() {
|
|
26318
|
+
if (this.messageHandler) {
|
|
26319
|
+
window.removeEventListener('message', this.messageHandler);
|
|
26320
|
+
this.messageHandler = null;
|
|
26321
|
+
}
|
|
26322
|
+
if (this.checkClosedInterval) {
|
|
26323
|
+
clearInterval(this.checkClosedInterval);
|
|
26324
|
+
this.checkClosedInterval = null;
|
|
26325
|
+
}
|
|
26326
|
+
if (this.popup && !this.popup.closed) {
|
|
26327
|
+
this.popup.close();
|
|
26328
|
+
}
|
|
26329
|
+
this.popup = null;
|
|
26330
|
+
}
|
|
26331
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: OAuthService, deps: [{ token: VALTECH_AUTH_CONFIG }, { token: i1$8.HttpClient }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
26332
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: OAuthService, providedIn: 'root' }); }
|
|
26333
|
+
}
|
|
26334
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: OAuthService, decorators: [{
|
|
26335
|
+
type: Injectable,
|
|
26336
|
+
args: [{ providedIn: 'root' }]
|
|
26337
|
+
}], ctorParameters: () => [{ type: undefined, decorators: [{
|
|
26338
|
+
type: Inject,
|
|
26339
|
+
args: [VALTECH_AUTH_CONFIG]
|
|
26340
|
+
}] }, { type: i1$8.HttpClient }, { type: i0.NgZone }] });
|
|
26341
|
+
|
|
25258
26342
|
/**
|
|
25259
26343
|
* Servicio principal de autenticación.
|
|
25260
26344
|
*
|
|
@@ -25278,7 +26362,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
25278
26362
|
* ```
|
|
25279
26363
|
*/
|
|
25280
26364
|
class AuthService {
|
|
25281
|
-
constructor(config, http, router, stateService, tokenService, storageService, syncService, firebaseService, messagingService, i18nService) {
|
|
26365
|
+
constructor(config, http, router, stateService, tokenService, storageService, syncService, firebaseService, oauthService, messagingService, i18nService) {
|
|
25282
26366
|
this.config = config;
|
|
25283
26367
|
this.http = http;
|
|
25284
26368
|
this.router = router;
|
|
@@ -25287,6 +26371,7 @@ class AuthService {
|
|
|
25287
26371
|
this.storageService = storageService;
|
|
25288
26372
|
this.syncService = syncService;
|
|
25289
26373
|
this.firebaseService = firebaseService;
|
|
26374
|
+
this.oauthService = oauthService;
|
|
25290
26375
|
this.messagingService = messagingService;
|
|
25291
26376
|
this.i18nService = i18nService;
|
|
25292
26377
|
// Timer para refresh proactivo
|
|
@@ -25400,6 +26485,101 @@ class AuthService {
|
|
|
25400
26485
|
}
|
|
25401
26486
|
}), catchError(error => this.handleAuthError(error)));
|
|
25402
26487
|
}
|
|
26488
|
+
// =============================================
|
|
26489
|
+
// OAUTH (Login social)
|
|
26490
|
+
// =============================================
|
|
26491
|
+
/**
|
|
26492
|
+
* Inicia sesión con OAuth (Google, Apple, Microsoft).
|
|
26493
|
+
* Abre un popup para el flujo de autenticación.
|
|
26494
|
+
*
|
|
26495
|
+
* @param provider - Proveedor OAuth ('google', 'apple', 'microsoft')
|
|
26496
|
+
* @returns Observable que emite SigninResponse cuando se completa
|
|
26497
|
+
*
|
|
26498
|
+
* @example
|
|
26499
|
+
* ```typescript
|
|
26500
|
+
* this.auth.signinWithOAuth('google').subscribe({
|
|
26501
|
+
* next: () => this.router.navigate(['/']),
|
|
26502
|
+
* error: (err) => console.error('OAuth failed:', err)
|
|
26503
|
+
* });
|
|
26504
|
+
* ```
|
|
26505
|
+
*/
|
|
26506
|
+
signinWithOAuth(provider) {
|
|
26507
|
+
this.stateService.clearError();
|
|
26508
|
+
return this.oauthService.startFlow(provider).pipe(tap(result => {
|
|
26509
|
+
// Convertir OAuthResult a SigninResponse compatible
|
|
26510
|
+
const response = {
|
|
26511
|
+
operationId: 'oauth',
|
|
26512
|
+
accessToken: result.accessToken,
|
|
26513
|
+
refreshToken: result.refreshToken,
|
|
26514
|
+
firebaseToken: result.firebaseToken,
|
|
26515
|
+
expiresIn: result.expiresIn,
|
|
26516
|
+
roles: result.roles,
|
|
26517
|
+
permissions: result.permissions,
|
|
26518
|
+
};
|
|
26519
|
+
this.handleSuccessfulAuth(response);
|
|
26520
|
+
}), map$1(result => ({
|
|
26521
|
+
operationId: 'oauth',
|
|
26522
|
+
accessToken: result.accessToken,
|
|
26523
|
+
refreshToken: result.refreshToken,
|
|
26524
|
+
firebaseToken: result.firebaseToken,
|
|
26525
|
+
expiresIn: result.expiresIn,
|
|
26526
|
+
roles: result.roles,
|
|
26527
|
+
permissions: result.permissions,
|
|
26528
|
+
})), catchError(error => {
|
|
26529
|
+
const authError = {
|
|
26530
|
+
code: error.code || 'OAUTH_ERROR',
|
|
26531
|
+
message: error.message || 'Error de autenticación OAuth',
|
|
26532
|
+
};
|
|
26533
|
+
this.stateService.setError(authError);
|
|
26534
|
+
return throwError(() => authError);
|
|
26535
|
+
}));
|
|
26536
|
+
}
|
|
26537
|
+
/**
|
|
26538
|
+
* Vincula un proveedor OAuth adicional a la cuenta actual.
|
|
26539
|
+
* Requiere que el usuario esté autenticado.
|
|
26540
|
+
*
|
|
26541
|
+
* @param provider - Proveedor OAuth a vincular
|
|
26542
|
+
*/
|
|
26543
|
+
linkOAuthProvider(provider) {
|
|
26544
|
+
return this.oauthService.startLinkFlow(provider).pipe(map$1(() => ({ success: true })), catchError(error => {
|
|
26545
|
+
const authError = {
|
|
26546
|
+
code: error.code || 'LINK_ERROR',
|
|
26547
|
+
message: error.message || 'Error al vincular proveedor',
|
|
26548
|
+
};
|
|
26549
|
+
this.stateService.setError(authError);
|
|
26550
|
+
return throwError(() => authError);
|
|
26551
|
+
}));
|
|
26552
|
+
}
|
|
26553
|
+
/**
|
|
26554
|
+
* Obtiene los proveedores OAuth vinculados al usuario.
|
|
26555
|
+
*/
|
|
26556
|
+
getLinkedProviders() {
|
|
26557
|
+
return this.oauthService.getLinkedProviders();
|
|
26558
|
+
}
|
|
26559
|
+
/**
|
|
26560
|
+
* Desvincula un proveedor OAuth de la cuenta.
|
|
26561
|
+
*
|
|
26562
|
+
* @param provider - Proveedor a desvincular
|
|
26563
|
+
*/
|
|
26564
|
+
unlinkOAuthProvider(provider) {
|
|
26565
|
+
return this.oauthService.unlinkProvider(provider);
|
|
26566
|
+
}
|
|
26567
|
+
/**
|
|
26568
|
+
* Establece contraseña para usuarios que solo tienen OAuth.
|
|
26569
|
+
* Permite que usuarios OAuth-only puedan también usar email/password.
|
|
26570
|
+
*
|
|
26571
|
+
* @param password - Nueva contraseña
|
|
26572
|
+
*/
|
|
26573
|
+
setPasswordForOAuthUser(password) {
|
|
26574
|
+
return this.oauthService.setPassword(password);
|
|
26575
|
+
}
|
|
26576
|
+
/**
|
|
26577
|
+
* Verifica si el usuario tiene contraseña establecida.
|
|
26578
|
+
* Útil para mostrar/ocultar opciones de cambio de contraseña.
|
|
26579
|
+
*/
|
|
26580
|
+
checkHasPassword() {
|
|
26581
|
+
return this.oauthService.hasPassword();
|
|
26582
|
+
}
|
|
25403
26583
|
/**
|
|
25404
26584
|
* Registra un nuevo usuario.
|
|
25405
26585
|
* El usuario queda en estado PENDING hasta verificar su email.
|
|
@@ -26093,7 +27273,7 @@ class AuthService {
|
|
|
26093
27273
|
}
|
|
26094
27274
|
return { platform: 'web', browser, os };
|
|
26095
27275
|
}
|
|
26096
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AuthService, deps: [{ token: VALTECH_AUTH_CONFIG }, { token: i1$8.HttpClient }, { token: i1$1.Router }, { token: AuthStateService }, { token: TokenService }, { token: AuthStorageService }, { token: AuthSyncService }, { token: FirebaseService }, { token: MessagingService, optional: true }, { token: I18nService, optional: true }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
27276
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AuthService, deps: [{ token: VALTECH_AUTH_CONFIG }, { token: i1$8.HttpClient }, { token: i1$1.Router }, { token: AuthStateService }, { token: TokenService }, { token: AuthStorageService }, { token: AuthSyncService }, { token: FirebaseService }, { token: OAuthService }, { token: MessagingService, optional: true }, { token: I18nService, optional: true }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
26097
27277
|
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AuthService, providedIn: 'root' }); }
|
|
26098
27278
|
}
|
|
26099
27279
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AuthService, decorators: [{
|
|
@@ -26102,7 +27282,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
26102
27282
|
}], ctorParameters: () => [{ type: undefined, decorators: [{
|
|
26103
27283
|
type: Inject,
|
|
26104
27284
|
args: [VALTECH_AUTH_CONFIG]
|
|
26105
|
-
}] }, { type: i1$8.HttpClient }, { type: i1$1.Router }, { type: AuthStateService }, { type: TokenService }, { type: AuthStorageService }, { type: AuthSyncService }, { type: FirebaseService }, { type: MessagingService, decorators: [{
|
|
27285
|
+
}] }, { type: i1$8.HttpClient }, { type: i1$1.Router }, { type: AuthStateService }, { type: TokenService }, { type: AuthStorageService }, { type: AuthSyncService }, { type: FirebaseService }, { type: OAuthService }, { type: MessagingService, decorators: [{
|
|
26106
27286
|
type: Optional
|
|
26107
27287
|
}] }, { type: I18nService, decorators: [{
|
|
26108
27288
|
type: Optional
|
|
@@ -26301,6 +27481,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
26301
27481
|
type: Optional
|
|
26302
27482
|
}] }] });
|
|
26303
27483
|
|
|
27484
|
+
/**
|
|
27485
|
+
* Analytics Types
|
|
27486
|
+
*
|
|
27487
|
+
* Tipos e interfaces para el servicio de Firebase Analytics (GA4).
|
|
27488
|
+
*/
|
|
27489
|
+
|
|
26304
27490
|
/**
|
|
26305
27491
|
* Firebase Services
|
|
26306
27492
|
*
|
|
@@ -26725,6 +27911,144 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
26725
27911
|
args: [VALTECH_AUTH_CONFIG]
|
|
26726
27912
|
}] }, { type: i1$8.HttpClient }] });
|
|
26727
27913
|
|
|
27914
|
+
/**
|
|
27915
|
+
* Componente de callback para OAuth.
|
|
27916
|
+
*
|
|
27917
|
+
* Este componente procesa la respuesta del servidor OAuth y envía
|
|
27918
|
+
* los tokens a la ventana padre via postMessage.
|
|
27919
|
+
*
|
|
27920
|
+
* Debe agregarse a las rutas de la aplicación:
|
|
27921
|
+
* ```typescript
|
|
27922
|
+
* // app.routes.ts
|
|
27923
|
+
* import { OAuthCallbackComponent } from 'valtech-components';
|
|
27924
|
+
*
|
|
27925
|
+
* export const routes: Routes = [
|
|
27926
|
+
* { path: 'auth/oauth/callback', component: OAuthCallbackComponent },
|
|
27927
|
+
* // ... otras rutas
|
|
27928
|
+
* ];
|
|
27929
|
+
* ```
|
|
27930
|
+
*
|
|
27931
|
+
* El backend redirige a esta ruta con los tokens en query params:
|
|
27932
|
+
* `/auth/oauth/callback?access_token=xxx&refresh_token=xxx&expires_in=900`
|
|
27933
|
+
*
|
|
27934
|
+
* O con error:
|
|
27935
|
+
* `/auth/oauth/callback?error=INVALID_CODE&error_description=...`
|
|
27936
|
+
*/
|
|
27937
|
+
class OAuthCallbackComponent {
|
|
27938
|
+
constructor() {
|
|
27939
|
+
this.message = 'Procesando autenticación...';
|
|
27940
|
+
}
|
|
27941
|
+
ngOnInit() {
|
|
27942
|
+
this.processCallback();
|
|
27943
|
+
}
|
|
27944
|
+
processCallback() {
|
|
27945
|
+
const params = new URLSearchParams(window.location.search);
|
|
27946
|
+
// Verificar si hay error
|
|
27947
|
+
const error = params.get('error');
|
|
27948
|
+
if (error) {
|
|
27949
|
+
this.sendToParent({
|
|
27950
|
+
type: 'oauth-callback',
|
|
27951
|
+
error: {
|
|
27952
|
+
code: error,
|
|
27953
|
+
message: params.get('error_description') || 'Error de autenticación',
|
|
27954
|
+
},
|
|
27955
|
+
});
|
|
27956
|
+
this.message = 'Error de autenticación';
|
|
27957
|
+
this.closeAfterDelay();
|
|
27958
|
+
return;
|
|
27959
|
+
}
|
|
27960
|
+
// Extraer tokens
|
|
27961
|
+
const accessToken = params.get('access_token');
|
|
27962
|
+
const refreshToken = params.get('refresh_token');
|
|
27963
|
+
const expiresIn = params.get('expires_in');
|
|
27964
|
+
const firebaseToken = params.get('firebase_token');
|
|
27965
|
+
if (!accessToken || !refreshToken) {
|
|
27966
|
+
this.sendToParent({
|
|
27967
|
+
type: 'oauth-callback',
|
|
27968
|
+
error: {
|
|
27969
|
+
code: 'MISSING_TOKENS',
|
|
27970
|
+
message: 'No se recibieron los tokens de autenticación',
|
|
27971
|
+
},
|
|
27972
|
+
});
|
|
27973
|
+
this.message = 'Error: tokens no recibidos';
|
|
27974
|
+
this.closeAfterDelay();
|
|
27975
|
+
return;
|
|
27976
|
+
}
|
|
27977
|
+
// Extraer roles y permisos (pueden venir como JSON en base64)
|
|
27978
|
+
let roles;
|
|
27979
|
+
let permissions;
|
|
27980
|
+
const rolesParam = params.get('roles');
|
|
27981
|
+
const permissionsParam = params.get('permissions');
|
|
27982
|
+
if (rolesParam) {
|
|
27983
|
+
try {
|
|
27984
|
+
roles = JSON.parse(atob(rolesParam));
|
|
27985
|
+
}
|
|
27986
|
+
catch {
|
|
27987
|
+
roles = rolesParam.split(',');
|
|
27988
|
+
}
|
|
27989
|
+
}
|
|
27990
|
+
if (permissionsParam) {
|
|
27991
|
+
try {
|
|
27992
|
+
permissions = JSON.parse(atob(permissionsParam));
|
|
27993
|
+
}
|
|
27994
|
+
catch {
|
|
27995
|
+
permissions = permissionsParam.split(',');
|
|
27996
|
+
}
|
|
27997
|
+
}
|
|
27998
|
+
// Enviar tokens a la ventana padre
|
|
27999
|
+
const result = {
|
|
28000
|
+
accessToken,
|
|
28001
|
+
refreshToken,
|
|
28002
|
+
expiresIn: expiresIn ? parseInt(expiresIn, 10) : 900,
|
|
28003
|
+
firebaseToken: firebaseToken || undefined,
|
|
28004
|
+
roles,
|
|
28005
|
+
permissions,
|
|
28006
|
+
isNewUser: params.get('is_new_user') === 'true',
|
|
28007
|
+
linked: params.get('linked') === 'true',
|
|
28008
|
+
};
|
|
28009
|
+
this.sendToParent({
|
|
28010
|
+
type: 'oauth-callback',
|
|
28011
|
+
tokens: result,
|
|
28012
|
+
});
|
|
28013
|
+
this.message = 'Autenticación exitosa';
|
|
28014
|
+
this.closeAfterDelay();
|
|
28015
|
+
}
|
|
28016
|
+
sendToParent(data) {
|
|
28017
|
+
if (window.opener) {
|
|
28018
|
+
// Enviar al opener (ventana que abrió el popup)
|
|
28019
|
+
window.opener.postMessage(data, window.location.origin);
|
|
28020
|
+
}
|
|
28021
|
+
else if (window.parent !== window) {
|
|
28022
|
+
// Enviar al parent (si estamos en iframe)
|
|
28023
|
+
window.parent.postMessage(data, window.location.origin);
|
|
28024
|
+
}
|
|
28025
|
+
}
|
|
28026
|
+
closeAfterDelay() {
|
|
28027
|
+
// Dar tiempo para que el mensaje se envíe antes de cerrar
|
|
28028
|
+
setTimeout(() => {
|
|
28029
|
+
if (window.opener) {
|
|
28030
|
+
window.close();
|
|
28031
|
+
}
|
|
28032
|
+
}, 500);
|
|
28033
|
+
}
|
|
28034
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: OAuthCallbackComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
28035
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: OAuthCallbackComponent, isStandalone: true, selector: "val-oauth-callback", ngImport: i0, template: `
|
|
28036
|
+
<div class="oauth-callback">
|
|
28037
|
+
<div class="oauth-callback__spinner"></div>
|
|
28038
|
+
<p class="oauth-callback__text">{{ message }}</p>
|
|
28039
|
+
</div>
|
|
28040
|
+
`, isInline: true, styles: [".oauth-callback{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100vh;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif}.oauth-callback__spinner{width:40px;height:40px;border:3px solid #f3f3f3;border-top:3px solid #3498db;border-radius:50%;animation:spin 1s linear infinite;margin-bottom:16px}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.oauth-callback__text{color:#666;font-size:14px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }] }); }
|
|
28041
|
+
}
|
|
28042
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: OAuthCallbackComponent, decorators: [{
|
|
28043
|
+
type: Component,
|
|
28044
|
+
args: [{ selector: 'val-oauth-callback', standalone: true, imports: [CommonModule], template: `
|
|
28045
|
+
<div class="oauth-callback">
|
|
28046
|
+
<div class="oauth-callback__spinner"></div>
|
|
28047
|
+
<p class="oauth-callback__text">{{ message }}</p>
|
|
28048
|
+
</div>
|
|
28049
|
+
`, styles: [".oauth-callback{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100vh;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif}.oauth-callback__spinner{width:40px;height:40px;border:3px solid #f3f3f3;border-top:3px solid #3498db;border-radius:50%;animation:spin 1s linear infinite;margin-bottom:16px}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.oauth-callback__text{color:#666;font-size:14px}\n"] }]
|
|
28050
|
+
}] });
|
|
28051
|
+
|
|
26728
28052
|
/**
|
|
26729
28053
|
* Valtech Auth Service
|
|
26730
28054
|
*
|
|
@@ -26953,5 +28277,5 @@ function provideValtechPresets(presets) {
|
|
|
26953
28277
|
* Generated bundle index. Do not edit.
|
|
26954
28278
|
*/
|
|
26955
28279
|
|
|
26956
|
-
export { ARTICLE_SPACING, AccordionComponent, ActionHeaderComponent, ActionType, AlertBoxComponent, ArticleBuilder, ArticleComponent, AuthService, AuthStateService, AuthStorageService, AuthSyncService, AvatarComponent, BannerComponent, BaseDefault, BoxComponent, BreadcrumbComponent, ButtonComponent, ButtonGroupComponent, COMMON_COUNTRY_CODES, COMMON_CURRENCIES, CURRENCY_INFO, CardComponent, CardSection, CardType, CardsCarouselComponent, CheckInputComponent, ChipGroupComponent, ClearDefault, ClearDefaultBlock, ClearDefaultFull, ClearDefaultRound, ClearDefaultRoundBlock, ClearDefaultRoundFull, CodeDisplayComponent, CommandDisplayComponent, CommentComponent, CommentInputComponent, CommentSectionComponent, CompanyFooterComponent, ComponentStates, ConfirmationDialogService, ContentLoaderComponent, CountdownComponent, CurrencyInputComponent, DEFAULT_AUTH_CONFIG, DEFAULT_CANCEL_BUTTON, DEFAULT_CONFIRM_BUTTON, DEFAULT_COUNTDOWN_LABELS, DEFAULT_COUNTDOWN_LABELS_EN, DEFAULT_EMPTY_STATE, DEFAULT_LEGEND_LABELS, DEFAULT_MODAL_CANCEL_BUTTON, DEFAULT_MODAL_CONFIRM_BUTTON, DEFAULT_PAGE_SIZE_OPTIONS, DEFAULT_PAYMENT_STATUS_COLORS, DEFAULT_PAYMENT_STATUS_LABELS, DEFAULT_PLATFORMS, DEFAULT_STATUS_COLORS, DEFAULT_STATUS_LABELS, DEFAULT_WINNER_LABELS, DataTableComponent, DateInputComponent, DateRangeInputComponent, DeviceService, DisplayComponent, DividerComponent, DownloadService, EmailInputComponent, ExpandableTextComponent, FabComponent, FileInputComponent, FirebaseService, FirestoreCollectionFactory, FirestoreService, FooterComponent, FooterLinksComponent, FormComponent, FormFooterComponent, FunHeaderComponent, GlowCardComponent, HeaderComponent, HintComponent, HorizontalScrollComponent, HourInputComponent, HrefComponent, I18nService, INITIAL_AUTH_STATE, INITIAL_MFA_STATE, Icon, IconComponent, IconService, ImageComponent, InAppBrowserService, InfoComponent, InputType, ItemListComponent, LANG_STORAGE_KEY$1 as LANG_STORAGE_KEY, LanguageSelectorComponent, LayeredCardComponent, LayoutComponent, LinkComponent, LinkProcessorService, LinksAccordionComponent, LinksCakeComponent, LocalStorageService, LocaleService, MODAL_SIZES, MOTION, MenuComponent, MessagingService, ModalService, MultiSelectSearchComponent, NavigationService, NoContentComponent, NotesBoxComponent, NotificationsService, NumberFromToComponent, NumberInputComponent, NumberStepperComponent, OutlineDefault, OutlineDefaultBlock, OutlineDefaultFull, OutlineDefaultRound, OutlineDefaultRoundBlock, OutlineDefaultRoundFull, PLATFORM_CONFIGS, PageContentComponent, PageTemplateComponent, PageWrapperComponent, PaginationComponent, ParticipantCardComponent, PasswordInputComponent, PhoneInputComponent, PillComponent, PinInputComponent, PlainCodeBoxComponent, PopoverSelectorComponent, PresetService, PriceTagComponent, PrimarySolidBlockButton, PrimarySolidBlockHrefButton, PrimarySolidBlockIconButton, PrimarySolidBlockIconHrefButton, PrimarySolidDefaultRoundButton, PrimarySolidDefaultRoundHrefButton, PrimarySolidDefaultRoundIconButton, PrimarySolidDefaultRoundIconHrefButton, PrimarySolidFullButton, PrimarySolidFullHrefButton, PrimarySolidFullIconButton, PrimarySolidFullIconHrefButton, PrimarySolidLargeRoundButton, PrimarySolidLargeRoundHrefButton, PrimarySolidLargeRoundIconButton, PrimarySolidLargeRoundIconHrefButton, PrimarySolidSmallRoundButton, PrimarySolidSmallRoundHrefButton, PrimarySolidSmallRoundIconButton, PrimarySolidSmallRoundIconHrefButton, ProcessLinksPipe, ProgressBarComponent, ProgressRingComponent, ProgressStatusComponent, PrompterComponent, QR_PRESETS, QrCodeComponent, QrGeneratorService, QueryBuilder, QuoteBoxComponent, RadioInputComponent, RaffleStatusCardComponent, RangeInputComponent, RatingComponent, RecapCardComponent, RightsFooterComponent, SKELETON_PRESETS, SearchSelectorComponent, SearchbarComponent, SecondarySolidBlockButton, SecondarySolidBlockHrefButton, SecondarySolidBlockIconButton, SecondarySolidBlockIconHrefButton, SecondarySolidDefaultRoundButton, SecondarySolidDefaultRoundHrefButton, SecondarySolidDefaultRoundIconButton, SecondarySolidDefaultRoundIconHrefButton, SecondarySolidFullButton, SecondarySolidFullHrefButton, SecondarySolidFullIconButton, SecondarySolidFullIconHrefButton, SecondarySolidLargeRoundButton, SecondarySolidLargeRoundHrefButton, SecondarySolidLargeRoundIconButton, SecondarySolidLargeRoundIconHrefButton, SecondarySolidSmallRoundButton, SecondarySolidSmallRoundHrefButton, SecondarySolidSmallRoundIconButton, SecondarySolidSmallRoundIconHrefButton, SegmentControlComponent, SelectSearchComponent, SessionService, ShareButtonsComponent, SimpleComponent, SkeletonComponent, SolidBlockButton, SolidDefault, SolidDefaultBlock, SolidDefaultButton, SolidDefaultFull, SolidDefaultRound, SolidDefaultRoundBlock, SolidDefaultRoundButton, SolidDefaultRoundFull, SolidFullButton, SolidLargeButton, SolidLargeRoundButton, SolidSmallButton, SolidSmallRoundButton, StatsCardComponent, StepperComponent, StorageService, SwipeCarouselComponent, TabbedContentComponent, TabsComponent, TestimonialCardComponent, TestimonialCarouselComponent, TextComponent, TextInputComponent, TextareaInputComponent, ThemeOption, ThemeService, TicketGridComponent, TimelineComponent, TitleBlockComponent, TitleComponent, ToastService, ToggleInputComponent, TokenService, ToolbarActionType, ToolbarComponent, TranslatePipe, TypedCollection, VALTECH_AUTH_CONFIG, VALTECH_FIREBASE_CONFIG, WinnerDisplayComponent, WizardComponent, WizardFooterComponent, applyDefaultValueToControl, authGuard, authInterceptor, buildPath, collections, createFirebaseConfig, createGlowCardProps, createNumberFromToField, createTitleProps, extractPathParams, getCollectionPath, getDocumentId, goToTop, guestGuard, hasEmulators, isAtEnd, isCollectionPath, isDocumentPath, isEmulatorMode, isValidPath, joinPath, maxLength, permissionGuard, permissionGuardFromRoute, provideValtechAuth, provideValtechAuthInterceptor, provideValtechFirebase, provideValtechI18n, provideValtechPresets, query, replaceSpecialChars, resolveColor, resolveInputDefaultValue, roleGuard, storagePaths, superAdminGuard };
|
|
28280
|
+
export { ARTICLE_SPACING, AccordionComponent, ActionHeaderComponent, ActionType, AlertBoxComponent, AnalyticsErrorHandler, AnalyticsRouterTracker, AnalyticsService, ArticleBuilder, ArticleComponent, AuthService, AuthStateService, AuthStorageService, AuthSyncService, AvatarComponent, BannerComponent, BaseDefault, BoxComponent, BreadcrumbComponent, ButtonComponent, ButtonGroupComponent, COMMON_COUNTRY_CODES, COMMON_CURRENCIES, CURRENCY_INFO, CardComponent, CardSection, CardType, CardsCarouselComponent, CheckInputComponent, ChipGroupComponent, ClearDefault, ClearDefaultBlock, ClearDefaultFull, ClearDefaultRound, ClearDefaultRoundBlock, ClearDefaultRoundFull, CodeDisplayComponent, CommandDisplayComponent, CommentComponent, CommentInputComponent, CommentSectionComponent, CompanyFooterComponent, ComponentStates, ConfirmationDialogService, ContentLoaderComponent, CountdownComponent, CurrencyInputComponent, DEFAULT_AUTH_CONFIG, DEFAULT_CANCEL_BUTTON, DEFAULT_CONFIRM_BUTTON, DEFAULT_COUNTDOWN_LABELS, DEFAULT_COUNTDOWN_LABELS_EN, DEFAULT_EMPTY_STATE, DEFAULT_LEGEND_LABELS, DEFAULT_MODAL_CANCEL_BUTTON, DEFAULT_MODAL_CONFIRM_BUTTON, DEFAULT_PAGE_SIZE_OPTIONS, DEFAULT_PAYMENT_STATUS_COLORS, DEFAULT_PAYMENT_STATUS_LABELS, DEFAULT_PLATFORMS, DEFAULT_STATUS_COLORS, DEFAULT_STATUS_LABELS, DEFAULT_WINNER_LABELS, DataTableComponent, DateInputComponent, DateRangeInputComponent, DeviceService, DisplayComponent, DividerComponent, DownloadService, EmailInputComponent, ExpandableTextComponent, FabComponent, FileInputComponent, FirebaseService, FirestoreCollectionFactory, FirestoreService, FooterComponent, FooterLinksComponent, FormComponent, FormFooterComponent, FunHeaderComponent, GlowCardComponent, HeaderComponent, HintComponent, HorizontalScrollComponent, HourInputComponent, HrefComponent, I18nService, INITIAL_AUTH_STATE, INITIAL_MFA_STATE, Icon, IconComponent, IconService, ImageComponent, InAppBrowserService, InfoComponent, InputType, ItemListComponent, LANG_STORAGE_KEY$1 as LANG_STORAGE_KEY, LanguageSelectorComponent, LayeredCardComponent, LayoutComponent, LinkComponent, LinkProcessorService, LinksAccordionComponent, LinksCakeComponent, LocalStorageService, LocaleService, MODAL_SIZES, MOTION, MenuComponent, MessagingService, ModalService, MultiSelectSearchComponent, NavigationService, NoContentComponent, NotesBoxComponent, NotificationsService, NumberFromToComponent, NumberInputComponent, NumberStepperComponent, OAuthCallbackComponent, OAuthService, OutlineDefault, OutlineDefaultBlock, OutlineDefaultFull, OutlineDefaultRound, OutlineDefaultRoundBlock, OutlineDefaultRoundFull, PLATFORM_CONFIGS, PageContentComponent, PageTemplateComponent, PageWrapperComponent, PaginationComponent, ParticipantCardComponent, PasswordInputComponent, PhoneInputComponent, PillComponent, PinInputComponent, PlainCodeBoxComponent, PopoverSelectorComponent, PresetService, PriceTagComponent, PrimarySolidBlockButton, PrimarySolidBlockHrefButton, PrimarySolidBlockIconButton, PrimarySolidBlockIconHrefButton, PrimarySolidDefaultRoundButton, PrimarySolidDefaultRoundHrefButton, PrimarySolidDefaultRoundIconButton, PrimarySolidDefaultRoundIconHrefButton, PrimarySolidFullButton, PrimarySolidFullHrefButton, PrimarySolidFullIconButton, PrimarySolidFullIconHrefButton, PrimarySolidLargeRoundButton, PrimarySolidLargeRoundHrefButton, PrimarySolidLargeRoundIconButton, PrimarySolidLargeRoundIconHrefButton, PrimarySolidSmallRoundButton, PrimarySolidSmallRoundHrefButton, PrimarySolidSmallRoundIconButton, PrimarySolidSmallRoundIconHrefButton, ProcessLinksPipe, ProgressBarComponent, ProgressRingComponent, ProgressStatusComponent, PrompterComponent, QR_PRESETS, QrCodeComponent, QrGeneratorService, QueryBuilder, QuoteBoxComponent, RadioInputComponent, RaffleStatusCardComponent, RangeInputComponent, RatingComponent, RecapCardComponent, RightsFooterComponent, SKELETON_PRESETS, SearchSelectorComponent, SearchbarComponent, SecondarySolidBlockButton, SecondarySolidBlockHrefButton, SecondarySolidBlockIconButton, SecondarySolidBlockIconHrefButton, SecondarySolidDefaultRoundButton, SecondarySolidDefaultRoundHrefButton, SecondarySolidDefaultRoundIconButton, SecondarySolidDefaultRoundIconHrefButton, SecondarySolidFullButton, SecondarySolidFullHrefButton, SecondarySolidFullIconButton, SecondarySolidFullIconHrefButton, SecondarySolidLargeRoundButton, SecondarySolidLargeRoundHrefButton, SecondarySolidLargeRoundIconButton, SecondarySolidLargeRoundIconHrefButton, SecondarySolidSmallRoundButton, SecondarySolidSmallRoundHrefButton, SecondarySolidSmallRoundIconButton, SecondarySolidSmallRoundIconHrefButton, SegmentControlComponent, SelectSearchComponent, SessionService, ShareButtonsComponent, SimpleComponent, SkeletonComponent, SolidBlockButton, SolidDefault, SolidDefaultBlock, SolidDefaultButton, SolidDefaultFull, SolidDefaultRound, SolidDefaultRoundBlock, SolidDefaultRoundButton, SolidDefaultRoundFull, SolidFullButton, SolidLargeButton, SolidLargeRoundButton, SolidSmallButton, SolidSmallRoundButton, StatsCardComponent, StepperComponent, StorageService, SwipeCarouselComponent, TabbedContentComponent, TabsComponent, TestimonialCardComponent, TestimonialCarouselComponent, TextComponent, TextInputComponent, TextareaInputComponent, ThemeOption, ThemeService, TicketGridComponent, TimelineComponent, TitleBlockComponent, TitleComponent, ToastService, ToggleInputComponent, TokenService, ToolbarActionType, ToolbarComponent, TranslatePipe, TypedCollection, VALTECH_AUTH_CONFIG, VALTECH_FIREBASE_CONFIG, WinnerDisplayComponent, WizardComponent, WizardFooterComponent, applyDefaultValueToControl, authGuard, authInterceptor, buildPath, collections, createFirebaseConfig, createGlowCardProps, createNumberFromToField, createTitleProps, extractPathParams, getCollectionPath, getDocumentId, goToTop, guestGuard, hasEmulators, isAtEnd, isCollectionPath, isDocumentPath, isEmulatorMode, isValidPath, joinPath, maxLength, permissionGuard, permissionGuardFromRoute, provideValtechAuth, provideValtechAuthInterceptor, provideValtechFirebase, provideValtechI18n, provideValtechPresets, query, replaceSpecialChars, resolveColor, resolveInputDefaultValue, roleGuard, storagePaths, superAdminGuard };
|
|
26957
28281
|
//# sourceMappingURL=valtech-components.mjs.map
|