valtech-components 2.0.725 → 2.0.727

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.
@@ -41,7 +41,7 @@ import { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop';
41
41
  import { filter, switchMap as switchMap$1, catchError as catchError$1, tap, map as map$1, finalize, take, debounceTime as debounceTime$1, takeUntil as takeUntil$1 } from 'rxjs/operators';
42
42
  import { ImageCropperComponent } from 'ngx-image-cropper';
43
43
  import * as i1$8 from '@angular/common/http';
44
- import { provideHttpClient, withInterceptors, HttpClient } from '@angular/common/http';
44
+ import { provideHttpClient, withInterceptors, HttpErrorResponse, HttpClient } from '@angular/common/http';
45
45
  import { Capacitor } from '@capacitor/core';
46
46
  import 'prismjs/components/prism-scss';
47
47
  import 'prismjs/components/prism-json';
@@ -50,7 +50,7 @@ import 'prismjs/components/prism-json';
50
50
  * Current version of valtech-components.
51
51
  * This is automatically updated during the publish process.
52
52
  */
53
- const VERSION = '2.0.725';
53
+ const VERSION = '2.0.727';
54
54
 
55
55
  /**
56
56
  * Servicio para gestionar presets de componentes.
@@ -17863,6 +17863,19 @@ function joinPath(...segments) {
17863
17863
  * Servicio genérico para operaciones CRUD en Firestore.
17864
17864
  * Soporta lecturas one-time, subscripciones real-time, paginación y queries complejas.
17865
17865
  */
17866
+ /**
17867
+ * Internal sentinel used to mark a collection path as global cross-app —
17868
+ * i.e. it should NOT be prefixed with `apps/{appId}/`.
17869
+ *
17870
+ * Apps consume this via `CollectionOptions.skipAppPrefix`; the sentinel
17871
+ * stays in `TypedCollection` and is stripped here before Firestore sees it.
17872
+ *
17873
+ * Format keeps it impossible to collide with a real Firestore path (`:` is
17874
+ * not allowed in collection segments).
17875
+ *
17876
+ * @internal
17877
+ */
17878
+ const ABS_PATH_SENTINEL = '__abs__:';
17866
17879
  /**
17867
17880
  * Servicio para operaciones CRUD en Firestore.
17868
17881
  *
@@ -17905,9 +17918,17 @@ class FirestoreService {
17905
17918
  * Prefija el path de colección con el appId si está configurado.
17906
17919
  * Si no hay appId, retorna el path sin modificar (backward compatible).
17907
17920
  *
17921
+ * Si el path empieza con `ABS_PATH_SENTINEL` (`__abs__:`), se asume que el
17922
+ * caller quiere un path global cross-app (ej. `users/{uid}/notifications` —
17923
+ * el inbox global de notificaciones). El sentinel se strippea y el resto
17924
+ * del path se pasa verbatim, sin prefijo `apps/{appId}/`.
17925
+ *
17908
17926
  * @internal
17909
17927
  */
17910
17928
  prefixCollectionPath(collectionPath) {
17929
+ if (collectionPath.startsWith(ABS_PATH_SENTINEL)) {
17930
+ return collectionPath.slice(ABS_PATH_SENTINEL.length);
17931
+ }
17911
17932
  if (!this.config?.appId)
17912
17933
  return collectionPath;
17913
17934
  return `apps/${this.config.appId}/${collectionPath}`;
@@ -25930,57 +25951,90 @@ const DEFAULT_EMULATOR_CONFIG = {
25930
25951
  // STORAGE PATH BUILDERS
25931
25952
  // ============================================================================
25932
25953
  /**
25933
- * Genera paths de Storage con namespace por app.
25954
+ * Genera paths de Storage alineados con storage.rules.
25955
+ *
25956
+ * Convenciones (ver frontend/firebase/storage.rules):
25957
+ * - Avatar global del user (cross-app): /users/{uid}/avatar.jpg
25958
+ * - Files privados del user (cross-app): /users/{uid}/files/{path}
25959
+ * - Avatar per-app del user: /apps/{appId}/users/{uid}/avatar.jpg
25960
+ * - Files per-app del user: /apps/{appId}/users/{uid}/files/{path}
25961
+ * - Public per-app: /apps/{appId}/public/{path}
25962
+ * - Shared per-app (auth-only): /apps/{appId}/shared/{path}
25963
+ * - Public global: /public/{path}
25934
25964
  *
25935
25965
  * @example
25936
- * // Path específico de la app
25937
- * storagePaths.forApp('showcase', 'uploads', 'image.jpg')
25938
- * // => 'showcase/uploads/image.jpg'
25966
+ * storagePaths.forApp('showcase', 'public', 'banner.jpg')
25967
+ * // => 'apps/showcase/public/banner.jpg'
25968
+ *
25969
+ * storagePaths.userAvatar('user123')
25970
+ * // => 'users/user123/avatar.jpg'
25939
25971
  *
25940
- * // Path compartido
25941
- * storagePaths.shared.profilePhoto('user123', 'avatar.jpg')
25942
- * // => 'profile-photos/user123/avatar.jpg'
25972
+ * storagePaths.appUserFile('showcase', 'user123', 'doc.pdf')
25973
+ * // => 'apps/showcase/users/user123/files/doc.pdf'
25943
25974
  */
25944
25975
  const storagePaths = {
25945
- /** Carpeta específica de la app: {appId}/{...paths} */
25946
- forApp: (appId, ...paths) => [appId, ...paths].join('/'),
25947
- /** Carpetas compartidas (sin namespace) */
25948
- shared: {
25949
- /** Foto de perfil de usuario */
25950
- profilePhoto: (userId, fileName) => `profile-photos/${userId}/${fileName}`,
25951
- /** Archivos públicos accesibles sin autenticación */
25952
- public: (...paths) => `public/${paths.join('/')}`,
25953
- },
25976
+ /** Path per-app: apps/{appId}/{...paths} */
25977
+ forApp: (appId, ...paths) => ['apps', appId, ...paths].join('/'),
25978
+ /** Avatar global del user (cross-app) */
25979
+ userAvatar: (userId) => `users/${userId}/avatar.jpg`,
25980
+ /** Thumbnail global del user (cross-app) */
25981
+ userThumb: (userId) => `users/${userId}/thumb.jpg`,
25982
+ /** File privado global del user (cross-app) */
25983
+ userFile: (userId, ...paths) => ['users', userId, 'files', ...paths].join('/'),
25984
+ /** Avatar per-app del user */
25985
+ appUserAvatar: (appId, userId) => `apps/${appId}/users/${userId}/avatar.jpg`,
25986
+ /** File per-app del user */
25987
+ appUserFile: (appId, userId, ...paths) => ['apps', appId, 'users', userId, 'files', ...paths].join('/'),
25988
+ /** Public global (acceso sin auth, write admin-only) */
25989
+ public: (...paths) => `public/${paths.join('/')}`,
25954
25990
  };
25955
25991
  // ============================================================================
25956
25992
  // FIRESTORE COLLECTION BUILDERS
25957
25993
  // ============================================================================
25958
25994
  /**
25959
- * Genera paths de colecciones con namespace por app.
25995
+ * Genera paths de colecciones alineados con firestore.rules.
25960
25996
  *
25961
- * IMPORTANTE: La estructura es /apps/{appId}/{collection}/{docId}
25962
- * Firestore requiere número impar de segmentos para paths de colección.
25997
+ * Convenciones (ver frontend/firebase/firestore.rules):
25998
+ * - Cross-app shared:
25999
+ * /users/{uid} (privado), /profiles/{uid} (público global),
26000
+ * /users/{uid}/notifications (INBOX GLOBAL cross-app cross-org)
26001
+ * - Per-app:
26002
+ * /apps/{appId}/{collection}, /apps/{appId}/profiles/{uid}
26003
+ * - Per-app per-user (notifications NO viven aquí):
26004
+ * /apps/{appId}/users/{uid}/{collection}
26005
+ * - Per-app per-org:
26006
+ * /apps/{appId}/orgs/{orgId}/{collection}
25963
26007
  *
25964
26008
  * @example
25965
- * // Colección específica de la app
25966
26009
  * collections.forApp('showcase', 'items')
25967
26010
  * // => 'apps/showcase/items'
25968
26011
  *
25969
- * // Colección compartida
25970
- * collections.shared.users
25971
- * // => 'users'
26012
+ * collections.appUser('showcase', 'user123', 'drafts')
26013
+ * // => 'apps/showcase/users/user123/drafts'
26014
+ *
26015
+ * collections.appOrg('showcase', 'org_abc', 'posts')
26016
+ * // => 'apps/showcase/orgs/org_abc/posts'
26017
+ *
26018
+ * collections.userNotifications('user123')
26019
+ * // => 'users/user123/notifications'
25972
26020
  */
25973
26021
  const collections = {
25974
- /** Colección específica de la app: apps/{appId}/{collection} */
26022
+ /** Per-app cross-user: apps/{appId}/{collection} */
25975
26023
  forApp: (appId, collectionName) => `apps/${appId}/${collectionName}`,
25976
- /** Colecciones compartidas (sin namespace, nivel raíz) */
26024
+ /** Per-app per-user: apps/{appId}/users/{uid}/{collection} */
26025
+ appUser: (appId, userId, collectionName) => `apps/${appId}/users/${userId}/${collectionName}`,
26026
+ /** Per-app per-org: apps/{appId}/orgs/{orgId}/{collection} */
26027
+ appOrg: (appId, orgId, collectionName) => `apps/${appId}/orgs/${orgId}/${collectionName}`,
26028
+ /** Per-app perfil público del user: apps/{appId}/profiles/{uid} */
26029
+ appProfile: (appId, userId) => `apps/${appId}/profiles/${userId}`,
26030
+ /** Inbox global del user (cross-app cross-org) */
26031
+ userNotifications: (userId) => `users/${userId}/notifications`,
26032
+ /** Cross-app shared (sin namespace, nivel raíz) */
25977
26033
  shared: {
25978
- /** Usuarios del sistema */
26034
+ /** Doc privado del user — /users/{uid} */
25979
26035
  users: 'users',
25980
- /** Perfiles públicos */
26036
+ /** Perfiles públicos globales — /profiles/{uid} */
25981
26037
  profiles: 'profiles',
25982
- /** Notificaciones de usuarios */
25983
- notifications: 'notifications',
25984
26038
  },
25985
26039
  };
25986
26040
  /**
@@ -26541,12 +26595,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
26541
26595
  class TypedCollection {
26542
26596
  constructor(firestore, collectionPath, options = {}) {
26543
26597
  this.firestore = firestore;
26544
- this.collectionPath = collectionPath;
26545
26598
  this.options = {
26546
26599
  softDelete: false,
26547
26600
  timestamps: true,
26601
+ skipAppPrefix: false,
26548
26602
  ...options,
26549
26603
  };
26604
+ // Marcar el path con el sentinel cuando skipAppPrefix=true. FirestoreService
26605
+ // detecta el sentinel y devuelve el path sin prefijo `apps/{appId}/`.
26606
+ // Subcolecciones (subPath = `${this.collectionPath}/...`) heredan el sentinel
26607
+ // automáticamente por concatenación.
26608
+ this.collectionPath = this.options.skipAppPrefix
26609
+ ? `${ABS_PATH_SENTINEL}${collectionPath}`
26610
+ : collectionPath;
26550
26611
  }
26551
26612
  // ===========================================================================
26552
26613
  // LECTURAS ONE-TIME
@@ -28218,11 +28279,12 @@ class NotificationsService {
28218
28279
  return;
28219
28280
  }
28220
28281
  this.currentUserId = userId;
28221
- // Path relativo - FirestoreService agrega automáticamente apps/{appId}/
28222
- // NO agregar apps/ aquí para evitar doble prefijo
28282
+ // Path global cross-app: /users/{uid}/notifications (sin prefijo apps/{appId}/).
28283
+ // skipAppPrefix bypassa FirestoreService.prefixCollectionPath para fan-out compartido.
28223
28284
  const basePath = `users/${userId}/notifications`;
28224
28285
  this.collection = this.collectionFactory.create(basePath, {
28225
28286
  timestamps: true,
28287
+ skipAppPrefix: true,
28226
28288
  });
28227
28289
  this.collectionReady$.next(this.collection);
28228
28290
  console.log('[Notifications] Initialized with path:', basePath);
@@ -30782,6 +30844,386 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
30782
30844
  `, 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"] }]
30783
30845
  }] });
30784
30846
 
30847
+ /**
30848
+ * Name of the query param that carries the handoff token. Apps may override
30849
+ * via `detectAndExchangeHandoff({ tokenParam })` but the default keeps the
30850
+ * convention consistent across the factory.
30851
+ */
30852
+ const HANDOFF_TOKEN_PARAM = 'handoff';
30853
+ /**
30854
+ * Name of the query param that carries the post-exchange route.
30855
+ */
30856
+ const HANDOFF_ROUTE_PARAM = 'route';
30857
+ /**
30858
+ * HandoffService — cross-app session transfer.
30859
+ *
30860
+ * Implements the OAuth Authorization Code pattern, applied internally between
30861
+ * apps that share the same backend.
30862
+ *
30863
+ * **Origin app (user is authenticated):**
30864
+ *
30865
+ * ```typescript
30866
+ * const { token, route } = await firstValueFrom(
30867
+ * handoff.createHandoff({ targetAppId: 'myvaltech', route: '/app/dashboard' })
30868
+ * );
30869
+ * window.location.href = `https://myvaltech.app/?handoff=${token}&route=${encodeURIComponent(route ?? '/')}`;
30870
+ * ```
30871
+ *
30872
+ * **Target app (boot):** call `exchangeHandoff(token)`. On success, the session
30873
+ * is installed (`AuthService.setExternalAuth`) and the user is authenticated.
30874
+ * See `detectAndExchangeHandoff` helper for the typical bootstrap pattern.
30875
+ *
30876
+ * Security notes:
30877
+ * - Token is single-use and short-lived (30s) — enforced server-side.
30878
+ * - Token in URL must be removed from history after exchange to avoid log leakage.
30879
+ * - The exchange endpoint is public; do NOT add auth header. The
30880
+ * `HttpClient` request below avoids triggering the auth interceptor since the
30881
+ * user isn't logged in yet on the target app.
30882
+ */
30883
+ class HandoffService {
30884
+ constructor(config, http, auth, router) {
30885
+ this.config = config;
30886
+ this.http = http;
30887
+ this.auth = auth;
30888
+ this.router = router;
30889
+ this.detected = false;
30890
+ }
30891
+ /**
30892
+ * Create a handoff token. Caller must be authenticated.
30893
+ *
30894
+ * @param request Optional metadata: target app id and intended route.
30895
+ * @returns Observable emitting `{ token, expiresAt, route? }`.
30896
+ */
30897
+ createHandoff(request = {}) {
30898
+ return this.http.post(`${this.baseUrl}/handoff`, request);
30899
+ }
30900
+ /**
30901
+ * Exchange a handoff token for a session and install it.
30902
+ *
30903
+ * On success, the response is piped through `AuthService.setExternalAuth`
30904
+ * so the user becomes authenticated. Subsequent navigation can proceed.
30905
+ *
30906
+ * On failure, the observable errors. The caller is responsible for
30907
+ * showing an error and routing to `/login`.
30908
+ *
30909
+ * @param token Raw handoff token read from the URL.
30910
+ */
30911
+ exchangeHandoff(token) {
30912
+ return this.http
30913
+ .post(`${this.baseUrl}/handoff/exchange`, { token })
30914
+ .pipe(tap(response => this.installSession(response)), catchError$1(err => {
30915
+ // Surface the error untouched — the caller decides how to react
30916
+ // (typically: redirect to /login + toast). Don't install a bad session.
30917
+ throw err;
30918
+ }));
30919
+ }
30920
+ /**
30921
+ * Bootstrap helper — reads the handoff token from the current URL, exchanges
30922
+ * it for a session, and navigates to the intended route with the token
30923
+ * stripped from history.
30924
+ *
30925
+ * Idempotent: subsequent calls are no-ops. Wire from an
30926
+ * `APP_INITIALIZER`/`provideAppInitializer` factory in `main.ts`.
30927
+ *
30928
+ * ```typescript
30929
+ * // main.ts
30930
+ * provideAppInitializer(() => inject(HandoffService).detectAndExchangeHandoff())
30931
+ * ```
30932
+ *
30933
+ * On error (expired/used/invalid token), redirects to `errorRoute` (default:
30934
+ * the app's configured `loginRoute`). The URL is always cleaned, even on
30935
+ * error, so the token cannot be retried by refreshing the page.
30936
+ *
30937
+ * @returns `true` if a handoff was detected and processed (success or fail).
30938
+ * `false` if no token was present (cold boot, normal flow).
30939
+ */
30940
+ async detectAndExchangeHandoff(options = {}) {
30941
+ if (this.detected || typeof window === 'undefined') {
30942
+ return false;
30943
+ }
30944
+ this.detected = true;
30945
+ const tokenParam = options.tokenParam ?? HANDOFF_TOKEN_PARAM;
30946
+ const routeParam = options.routeParam ?? HANDOFF_ROUTE_PARAM;
30947
+ const defaultRoute = options.defaultRoute ?? '/';
30948
+ const errorRoute = options.errorRoute ?? this.config.loginRoute ?? '/login';
30949
+ const params = new URLSearchParams(window.location.search);
30950
+ const token = params.get(tokenParam);
30951
+ if (!token) {
30952
+ return false;
30953
+ }
30954
+ const targetRoute = params.get(routeParam) || defaultRoute;
30955
+ try {
30956
+ await firstValueFrom(this.exchangeHandoff(token));
30957
+ await this.router.navigateByUrl(targetRoute, { replaceUrl: true });
30958
+ }
30959
+ catch (err) {
30960
+ console.warn('[Handoff] Exchange failed, redirecting to login', err);
30961
+ await this.router.navigateByUrl(errorRoute, { replaceUrl: true });
30962
+ }
30963
+ return true;
30964
+ }
30965
+ /**
30966
+ * Persist the session in `AuthService`. Mirrors the install flow used by
30967
+ * the normal signin path so timers, Firebase, and tab-sync all kick in.
30968
+ */
30969
+ installSession(response) {
30970
+ this.auth.setExternalAuth({
30971
+ accessToken: response.accessToken,
30972
+ refreshToken: response.refreshToken,
30973
+ firebaseToken: response.firebaseToken,
30974
+ expiresIn: response.expiresIn,
30975
+ });
30976
+ }
30977
+ get baseUrl() {
30978
+ return `${this.config.apiUrl}${this.config.authPrefix}`;
30979
+ }
30980
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: HandoffService, deps: [{ token: VALTECH_AUTH_CONFIG }, { token: i1$8.HttpClient }, { token: AuthService }, { token: i1$1.Router }], target: i0.ɵɵFactoryTarget.Injectable }); }
30981
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: HandoffService, providedIn: 'root' }); }
30982
+ }
30983
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: HandoffService, decorators: [{
30984
+ type: Injectable,
30985
+ args: [{ providedIn: 'root' }]
30986
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
30987
+ type: Inject,
30988
+ args: [VALTECH_AUTH_CONFIG]
30989
+ }] }, { type: i1$8.HttpClient }, { type: AuthService }, { type: i1$1.Router }] });
30990
+
30991
+ /**
30992
+ * OrgSwitchService — orchestrates active organization changes across the app.
30993
+ *
30994
+ * Built on top of `AuthService.switchOrg`, which already:
30995
+ * - Hits `POST /v2/auth/switch-org` and receives a new Firebase custom token.
30996
+ * - Re-authenticates Firebase Auth with the new token (RBAC claims update).
30997
+ * - Broadcasts `ORG_SWITCH` to other tabs via `AuthSyncService` (multi-tab sync).
30998
+ *
30999
+ * What this service adds on top:
31000
+ * - A `switching` signal so the UI can show a loading indicator (1-2s switch).
31001
+ * - An `orgChanged$` Observable that components subscribe to in order to
31002
+ * invalidate their org-scoped caches (e.g. drop old query results, reset
31003
+ * page state) without a full page reload.
31004
+ * - Optional `reload: true` for apps where invalidating in-memory state piece
31005
+ * by piece is impractical.
31006
+ *
31007
+ * **What this service does NOT do automatically:**
31008
+ *
31009
+ * 1. **Teardown Firestore listeners.** Listeners are owned by their subscribing
31010
+ * components (typically via `takeUntilDestroyed` or async pipe). When the
31011
+ * component re-renders or unsubscribes, the listener disposes. If a
31012
+ * component does NOT unsubscribe on org change, its listener will keep
31013
+ * pointing at the previous org's path and may start failing rules. The fix
31014
+ * is component-level: subscribe to `orgChanged$` and reset state, or use
31015
+ * the `reload: true` option.
31016
+ *
31017
+ * 2. **Re-instantiate routed components.** Angular keeps mounted components
31018
+ * alive across navigations. If you need fresh state, either subscribe to
31019
+ * `orgChanged$` in the component, or use `reload: true`.
31020
+ *
31021
+ * @example Basic switch with loading state
31022
+ * ```typescript
31023
+ * private orgSwitch = inject(OrgSwitchService);
31024
+ *
31025
+ * async onSwitchOrg(orgId: string) {
31026
+ * await this.orgSwitch.switchTo(orgId);
31027
+ * // Components subscribed to orgChanged$ have already reset their state.
31028
+ * }
31029
+ *
31030
+ * // In template:
31031
+ * @if (orgSwitch.switching()) { <val-loading /> }
31032
+ * ```
31033
+ *
31034
+ * @example Component reacting to org change
31035
+ * ```typescript
31036
+ * private orgSwitch = inject(OrgSwitchService);
31037
+ *
31038
+ * constructor() {
31039
+ * this.orgSwitch.orgChanged$
31040
+ * .pipe(takeUntilDestroyed())
31041
+ * .subscribe(() => this.resetState());
31042
+ * }
31043
+ * ```
31044
+ *
31045
+ * @example Brutal reload mode
31046
+ * ```typescript
31047
+ * await this.orgSwitch.switchTo(orgId, { reload: true });
31048
+ * // window.location.reload() — clean slate, loses scroll position
31049
+ * ```
31050
+ */
31051
+ class OrgSwitchService {
31052
+ constructor(auth) {
31053
+ this.auth = auth;
31054
+ this._switching = signal(false);
31055
+ this._orgChanged = new Subject();
31056
+ /**
31057
+ * `true` while a switch is in flight. UI should disable interactions
31058
+ * with org-scoped data and show a loading indicator.
31059
+ */
31060
+ this.switching = this._switching.asReadonly();
31061
+ /**
31062
+ * Fires after a successful switch, with the previous and new org ids.
31063
+ * Components subscribe to invalidate caches / reset state.
31064
+ *
31065
+ * Fires AFTER the Firebase re-auth completes — listeners attached here
31066
+ * see the updated Firebase user / activeOrg claim.
31067
+ */
31068
+ this.orgChanged$ = this._orgChanged.asObservable();
31069
+ }
31070
+ /**
31071
+ * Switch the user's active organization.
31072
+ *
31073
+ * Re-entrant safe: while a switch is in flight, additional calls are
31074
+ * rejected silently (returns immediately). Inspect `switching()` to gate UI.
31075
+ *
31076
+ * @param orgId Target organization id. Must be one the user has a role in
31077
+ * — backend rejects otherwise with `PERMISSION_DENIED`.
31078
+ * @param options See `SwitchOrgOptions`.
31079
+ *
31080
+ * @throws The error from `auth.switchOrg` if the backend call fails.
31081
+ * `switching` returns to `false` before the error propagates.
31082
+ */
31083
+ async switchTo(orgId, options = {}) {
31084
+ if (this._switching()) {
31085
+ return;
31086
+ }
31087
+ const previousOrg = this.currentActiveOrg();
31088
+ if (previousOrg === orgId) {
31089
+ // No-op — already on this org.
31090
+ return;
31091
+ }
31092
+ this._switching.set(true);
31093
+ try {
31094
+ await firstValueFrom(this.auth.switchOrg(orgId));
31095
+ this._orgChanged.next({ previousOrg, newOrg: orgId });
31096
+ if (options.reload && typeof window !== 'undefined') {
31097
+ window.location.reload();
31098
+ }
31099
+ }
31100
+ finally {
31101
+ this._switching.set(false);
31102
+ }
31103
+ }
31104
+ /**
31105
+ * Read the current `activeOrg` from the auth user signal.
31106
+ * Falls back to empty string if the user isn't loaded yet.
31107
+ */
31108
+ currentActiveOrg() {
31109
+ const user = this.auth.user();
31110
+ // AuthUser may expose activeOrgId under different names depending on
31111
+ // backend version — keep tolerant.
31112
+ return (user?.activeOrgId ??
31113
+ user?.activeOrg ??
31114
+ '');
31115
+ }
31116
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: OrgSwitchService, deps: [{ token: AuthService }], target: i0.ɵɵFactoryTarget.Injectable }); }
31117
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: OrgSwitchService, providedIn: 'root' }); }
31118
+ }
31119
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: OrgSwitchService, decorators: [{
31120
+ type: Injectable,
31121
+ args: [{ providedIn: 'root' }]
31122
+ }], ctorParameters: () => [{ type: AuthService }] });
31123
+
31124
+ /**
31125
+ * NotificationActionService — orquesta el "click en notificación" cross-app cross-org.
31126
+ *
31127
+ * Flujo:
31128
+ * 1. markAsRead (best-effort, no bloquea)
31129
+ * 2. Si appId destino !== appId actual → handoff token + redirect a app destino
31130
+ * 3. Si orgId destino !== activeOrg → switchTo(orgId) (sin reload, navega después)
31131
+ * 4. router.navigateByUrl(actionRoute)
31132
+ *
31133
+ * Requiere:
31134
+ * - ValtechAuthConfig.appId (identifica la app actual)
31135
+ * - ValtechAuthConfig.appUrls (mapa baseUrl para handoff cross-app)
31136
+ */
31137
+ class NotificationActionService {
31138
+ constructor(config, auth, orgSwitch, handoff, notifications, router) {
31139
+ this.config = config;
31140
+ this.auth = auth;
31141
+ this.orgSwitch = orgSwitch;
31142
+ this.handoff = handoff;
31143
+ this.notifications = notifications;
31144
+ this.router = router;
31145
+ }
31146
+ /**
31147
+ * Abre la notificación: marca como leída, switch-org si toca, navegación local o
31148
+ * redirect cross-app vía handoff.
31149
+ *
31150
+ * No lanza errores hacia la UI — devuelve un resultado descriptivo. Errores
31151
+ * técnicos quedan en console.warn.
31152
+ */
31153
+ async open(notif) {
31154
+ // 1) Mark as read (best-effort, no bloquea el flujo principal)
31155
+ if (notif.id && !notif.isRead) {
31156
+ this.notifications.markAsRead(notif.id).catch(err => {
31157
+ console.warn('[NotificationAction] markAsRead failed', err);
31158
+ });
31159
+ }
31160
+ const route = notif.actionRoute;
31161
+ if (!route) {
31162
+ return 'no-action-route';
31163
+ }
31164
+ const currentApp = this.config.appId;
31165
+ const targetApp = notif.appId;
31166
+ // 2) Cross-app: handoff + full redirect
31167
+ if (targetApp && currentApp && targetApp !== currentApp) {
31168
+ const baseUrl = this.config.appUrls?.[targetApp];
31169
+ if (!baseUrl) {
31170
+ console.warn(`[NotificationAction] Missing appUrls['${targetApp}'] — configure ValtechAuthConfig.appUrls`);
31171
+ return 'cross-app-unconfigured';
31172
+ }
31173
+ try {
31174
+ const resp = await firstValueFrom(this.handoff.createHandoff({ targetAppId: targetApp, route }));
31175
+ const url = this.buildHandoffUrl(baseUrl, resp.token, route);
31176
+ if (typeof window !== 'undefined') {
31177
+ window.location.href = url;
31178
+ }
31179
+ return 'redirected-cross-app';
31180
+ }
31181
+ catch (err) {
31182
+ const msg = err instanceof HttpErrorResponse ? err.message : String(err);
31183
+ console.warn('[NotificationAction] createHandoff failed:', msg);
31184
+ return 'handoff-failed';
31185
+ }
31186
+ }
31187
+ // 3) Same app — switch-org si toca (sin reload — la navegación posterior monta page fresh)
31188
+ let switched = false;
31189
+ if (notif.orgId) {
31190
+ const active = this.activeOrg();
31191
+ if (active && active !== notif.orgId) {
31192
+ await this.orgSwitch.switchTo(notif.orgId);
31193
+ switched = true;
31194
+ }
31195
+ }
31196
+ // 4) Navigate local
31197
+ await this.router.navigateByUrl(route);
31198
+ return switched ? 'navigated-after-switch-org' : 'navigated';
31199
+ }
31200
+ activeOrg() {
31201
+ const user = this.auth.user();
31202
+ return (user?.activeOrgId ??
31203
+ user?.activeOrg ??
31204
+ '');
31205
+ }
31206
+ /**
31207
+ * Construye URL absoluta para handoff cross-app preservando pathname y otros
31208
+ * params existentes de la baseUrl.
31209
+ */
31210
+ buildHandoffUrl(baseUrl, token, route) {
31211
+ const url = new URL(baseUrl);
31212
+ url.searchParams.set('handoff', token);
31213
+ url.searchParams.set('route', route);
31214
+ return url.toString();
31215
+ }
31216
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: NotificationActionService, deps: [{ token: VALTECH_AUTH_CONFIG }, { token: AuthService }, { token: OrgSwitchService }, { token: HandoffService }, { token: NotificationsService }, { token: i1$1.Router }], target: i0.ɵɵFactoryTarget.Injectable }); }
31217
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: NotificationActionService, providedIn: 'root' }); }
31218
+ }
31219
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: NotificationActionService, decorators: [{
31220
+ type: Injectable,
31221
+ args: [{ providedIn: 'root' }]
31222
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
31223
+ type: Inject,
31224
+ args: [VALTECH_AUTH_CONFIG]
31225
+ }] }, { type: AuthService }, { type: OrgSwitchService }, { type: HandoffService }, { type: NotificationsService }, { type: i1$1.Router }] });
31226
+
30785
31227
  /**
30786
31228
  * Valtech Auth Service
30787
31229
  *
@@ -41627,5 +42069,5 @@ function buildFooterLinks(links, t) {
41627
42069
  * Generated bundle index. Do not edit.
41628
42070
  */
41629
42071
 
41630
- export { ACTION_CARD_DEFAULTS, AD_SIZE_MAP, API_TABLE_COLUMN_LABELS, ARTICLE_SPACING, AVATAR_UPLOAD_DEFAULTS, AccordionComponent, ActionCardComponent, ActionHeaderComponent, ActionType, AdSlotComponent, AdsLoaderService, AdsService, AlertBoxComponent, AnalyticsErrorHandler, AnalyticsRouterTracker, AnalyticsService, AppConfigService, ArticleBuilder, ArticleComponent, AuthBackgroundComponent, AuthService, AuthStateService, AuthStorageService, AuthSyncService, AvatarComponent, AvatarUploadComponent, BOTTOM_NAV_DEFAULTS, BannerComponent, BaseDefault, BlogPostBuilder, BottomNavComponent, BoxComponent, BreadcrumbComponent, ButtonComponent, ButtonGroupComponent, COMMON_COUNTRY_CODES, COMMON_CURRENCIES, CURRENCY_INFO, CardComponent, CardSection, CardType, CardsCarouselComponent, CheckInputComponent, CheckboxRadioInputComponent, ChipGroupComponent, ClearDefault, ClearDefaultBlock, ClearDefaultFull, ClearDefaultRound, ClearDefaultRoundBlock, ClearDefaultRoundFull, CodeDisplayComponent, CommandDisplayComponent, CommentComponent, CommentInputComponent, CommentSectionComponent, CompanyFooterComponent, ComponentStates, ConfirmationDialogService, ContainerComponent, ContentLoaderComponent, ContentReactionComponent, ContentTransformer, CountdownComponent, CurrencyInputComponent, DEFAULT_ADS_CONFIG, DEFAULT_APP_CONFIG_SERVICE_CONFIG, DEFAULT_AUTH_CONFIG, DEFAULT_BACK_HEADER, DEFAULT_CANCEL_BUTTON, DEFAULT_CONFIRM_BUTTON, DEFAULT_COUNTDOWN_LABELS, DEFAULT_COUNTDOWN_LABELS_EN, DEFAULT_EMPTY_STATE, DEFAULT_EMULATOR_CONFIG, DEFAULT_FEEDBACK_CONFIG, DEFAULT_FEEDBACK_TYPE_OPTIONS, DEFAULT_HOME_HEADER, DEFAULT_INFINITE_LIST_METADATA, DEFAULT_MODAL_CANCEL_BUTTON, DEFAULT_MODAL_CONFIRM_BUTTON, DEFAULT_PAGE_SIZE_OPTIONS, DEFAULT_PLATFORMS, DEFAULT_REFRESHER_METADATA, DEFAULT_SKELETON_CONFIG, DataTableComponent, DateInputComponent, DateRangeInputComponent, DetailSkeletonComponent, DeviceService, DisplayComponent, DividerComponent, DocsApiTableComponent, DocsBreadcrumbComponent, DocsBuilder, DocsCalloutComponent, DocsCodeExampleComponent, DocsLayoutComponent, DocsNavLinksComponent, DocsNavigationService, DocsPageComponent, DocsSearchComponent, DocsSectionComponent, DocsShellComponent, DocsSidebarComponent, DocsTocComponent, DownloadService, EmailInputComponent, ExpandableTextComponent, FEATURES_LIST_DEFAULTS, FabComponent, FeaturesListComponent, FeedbackFormComponent, FeedbackService, FileInputComponent, FirebaseService, FirestoreCollectionFactory, FirestoreService, FooterComponent, FooterLinksComponent, FormComponent, FormFooterComponent, FormSkeletonComponent, FunHeaderComponent, GlowCardComponent, GridSkeletonComponent, HeaderComponent, HintComponent, HorizontalScrollComponent, HourInputComponent, HrefComponent, I18nService, IMAGE_DEFAULTS, INITIAL_AUTH_STATE, INITIAL_MFA_STATE, Icon, IconComponent, IconService, ImageComponent, ImageCropComponent, ImageService, InAppBrowserService, InfiniteListComponent, InfoComponent, InputI18nHelper, InputType, ItemListComponent, LANG_STORAGE_KEY$1 as LANG_STORAGE_KEY, LOGIN_DEFAULTS, LanguageSelectorComponent, LayeredCardComponent, LinkComponent, LinkProcessorService, LinkedProvidersComponent, LinksAccordionComponent, LinksCakeComponent, ListSkeletonComponent, LoadingDirective, LocalStorageService, LocaleService, LoginComponent, MODAL_SIZES, MOTION, MaintenancePageComponent, MenuComponent, MessagingService, MetaService, ModalService, MultiSelectSearchComponent, NavigationService, NewsBuilder, NoContentComponent, NotesBoxComponent, NotificationsService, NumberFromToComponent, NumberInputComponent, NumberStepperComponent, OAUTH_PROVIDERS_INFO, OAuthCallbackComponent, OAuthService, OutlineDefault, OutlineDefaultBlock, OutlineDefaultFull, OutlineDefaultRound, OutlineDefaultRoundBlock, OutlineDefaultRoundFull, PLATFORM_CONFIGS, PageContentComponent, PageTemplateComponent, PageWrapperComponent, PaginationComponent, PaginationService, 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, ProfileSkeletonComponent, ProgressBarComponent, ProgressRingComponent, ProgressStatusComponent, PrompterComponent, QR_PRESETS, QrCodeComponent, QrGeneratorService, QueryBuilder, QuoteBoxComponent, RadioInputComponent, RangeInputComponent, RatingComponent, RefresherComponent, RightsFooterComponent, RotatingTextComponent, 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, SkeletonService, SolidBlockButton, SolidDefault, SolidDefaultBlock, SolidDefaultButton, SolidDefaultFull, SolidDefaultRound, SolidDefaultRoundBlock, SolidDefaultRoundButton, SolidDefaultRoundFull, SolidFullButton, SolidLargeButton, SolidLargeRoundButton, SolidSmallButton, SolidSmallRoundButton, StatsCardComponent, StepperComponent, StorageService, SwipeCarouselComponent, TabbedContentComponent, TableSkeletonComponent, TabsComponent, Terminal404Component, TestimonialCardComponent, TestimonialCarouselComponent, TextComponent, TextInputComponent, TextareaInputComponent, ThemeOption, ThemeService, TimelineComponent, TitleBlockComponent, TitleComponent, ToastService, ToggleInputComponent, TokenService, ToolbarActionType, ToolbarComponent, TranslatePipe, TypedCollection, UpdateBannerComponent, UsernameInputComponent, VALTECH_ADS_CONFIG, VALTECH_APP_CONFIG, VALTECH_AUTH_CONFIG, VALTECH_COMPANY_LINKS, VALTECH_DEFAULT_CONTENT, VALTECH_FEEDBACK_CONFIG, VALTECH_FIREBASE_CONFIG, VALTECH_FOOTER_I18N, VALTECH_FOOTER_LOGO, VALTECH_LANGUAGE_SELECTOR, VALTECH_SOCIAL_LINKS, VERSION, WizardComponent, WizardFooterComponent, applyDefaultValueToControl, authGuard, authInterceptor, blogPost, buildFooterLinks, buildPath, collections, createFirebaseConfig, createGlowCardProps, createInitialPaginationState, createNumberFromToField, createTitleProps, docs, extractPathParams, getAppInfo, getAppVersion, getCollectionPath, getDocumentId, getTimeOfDayKey, goToTop, guestGuard, hasEmulators, isAtEnd, isCollectionPath, isDocumentPath, isEmulatorMode, isValidPath, joinPath, maxLength, news, permissionGuard, permissionGuardFromRoute, provideValtechAds, provideValtechAppConfig, provideValtechAuth, provideValtechAuthInterceptor, provideValtechFeedback, provideValtechFirebase, provideValtechI18n, provideValtechPresets, provideValtechSkeleton, query, replaceSpecialChars, resolveColor, resolveInputDefaultValue, roleGuard, storagePaths, superAdminGuard, toArticle };
42072
+ export { ACTION_CARD_DEFAULTS, AD_SIZE_MAP, API_TABLE_COLUMN_LABELS, ARTICLE_SPACING, AVATAR_UPLOAD_DEFAULTS, AccordionComponent, ActionCardComponent, ActionHeaderComponent, ActionType, AdSlotComponent, AdsLoaderService, AdsService, AlertBoxComponent, AnalyticsErrorHandler, AnalyticsRouterTracker, AnalyticsService, AppConfigService, ArticleBuilder, ArticleComponent, AuthBackgroundComponent, AuthService, AuthStateService, AuthStorageService, AuthSyncService, AvatarComponent, AvatarUploadComponent, BOTTOM_NAV_DEFAULTS, BannerComponent, BaseDefault, BlogPostBuilder, BottomNavComponent, BoxComponent, BreadcrumbComponent, ButtonComponent, ButtonGroupComponent, COMMON_COUNTRY_CODES, COMMON_CURRENCIES, CURRENCY_INFO, CardComponent, CardSection, CardType, CardsCarouselComponent, CheckInputComponent, CheckboxRadioInputComponent, ChipGroupComponent, ClearDefault, ClearDefaultBlock, ClearDefaultFull, ClearDefaultRound, ClearDefaultRoundBlock, ClearDefaultRoundFull, CodeDisplayComponent, CommandDisplayComponent, CommentComponent, CommentInputComponent, CommentSectionComponent, CompanyFooterComponent, ComponentStates, ConfirmationDialogService, ContainerComponent, ContentLoaderComponent, ContentReactionComponent, ContentTransformer, CountdownComponent, CurrencyInputComponent, DEFAULT_ADS_CONFIG, DEFAULT_APP_CONFIG_SERVICE_CONFIG, DEFAULT_AUTH_CONFIG, DEFAULT_BACK_HEADER, DEFAULT_CANCEL_BUTTON, DEFAULT_CONFIRM_BUTTON, DEFAULT_COUNTDOWN_LABELS, DEFAULT_COUNTDOWN_LABELS_EN, DEFAULT_EMPTY_STATE, DEFAULT_EMULATOR_CONFIG, DEFAULT_FEEDBACK_CONFIG, DEFAULT_FEEDBACK_TYPE_OPTIONS, DEFAULT_HOME_HEADER, DEFAULT_INFINITE_LIST_METADATA, DEFAULT_MODAL_CANCEL_BUTTON, DEFAULT_MODAL_CONFIRM_BUTTON, DEFAULT_PAGE_SIZE_OPTIONS, DEFAULT_PLATFORMS, DEFAULT_REFRESHER_METADATA, DEFAULT_SKELETON_CONFIG, DataTableComponent, DateInputComponent, DateRangeInputComponent, DetailSkeletonComponent, DeviceService, DisplayComponent, DividerComponent, DocsApiTableComponent, DocsBreadcrumbComponent, DocsBuilder, DocsCalloutComponent, DocsCodeExampleComponent, DocsLayoutComponent, DocsNavLinksComponent, DocsNavigationService, DocsPageComponent, DocsSearchComponent, DocsSectionComponent, DocsShellComponent, DocsSidebarComponent, DocsTocComponent, DownloadService, EmailInputComponent, ExpandableTextComponent, FEATURES_LIST_DEFAULTS, FabComponent, FeaturesListComponent, FeedbackFormComponent, FeedbackService, FileInputComponent, FirebaseService, FirestoreCollectionFactory, FirestoreService, FooterComponent, FooterLinksComponent, FormComponent, FormFooterComponent, FormSkeletonComponent, FunHeaderComponent, GlowCardComponent, GridSkeletonComponent, HANDOFF_ROUTE_PARAM, HANDOFF_TOKEN_PARAM, HandoffService, HeaderComponent, HintComponent, HorizontalScrollComponent, HourInputComponent, HrefComponent, I18nService, IMAGE_DEFAULTS, INITIAL_AUTH_STATE, INITIAL_MFA_STATE, Icon, IconComponent, IconService, ImageComponent, ImageCropComponent, ImageService, InAppBrowserService, InfiniteListComponent, InfoComponent, InputI18nHelper, InputType, ItemListComponent, LANG_STORAGE_KEY$1 as LANG_STORAGE_KEY, LOGIN_DEFAULTS, LanguageSelectorComponent, LayeredCardComponent, LinkComponent, LinkProcessorService, LinkedProvidersComponent, LinksAccordionComponent, LinksCakeComponent, ListSkeletonComponent, LoadingDirective, LocalStorageService, LocaleService, LoginComponent, MODAL_SIZES, MOTION, MaintenancePageComponent, MenuComponent, MessagingService, MetaService, ModalService, MultiSelectSearchComponent, NavigationService, NewsBuilder, NoContentComponent, NotesBoxComponent, NotificationActionService, NotificationsService, NumberFromToComponent, NumberInputComponent, NumberStepperComponent, OAUTH_PROVIDERS_INFO, OAuthCallbackComponent, OAuthService, OrgSwitchService, OutlineDefault, OutlineDefaultBlock, OutlineDefaultFull, OutlineDefaultRound, OutlineDefaultRoundBlock, OutlineDefaultRoundFull, PLATFORM_CONFIGS, PageContentComponent, PageTemplateComponent, PageWrapperComponent, PaginationComponent, PaginationService, 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, ProfileSkeletonComponent, ProgressBarComponent, ProgressRingComponent, ProgressStatusComponent, PrompterComponent, QR_PRESETS, QrCodeComponent, QrGeneratorService, QueryBuilder, QuoteBoxComponent, RadioInputComponent, RangeInputComponent, RatingComponent, RefresherComponent, RightsFooterComponent, RotatingTextComponent, 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, SkeletonService, SolidBlockButton, SolidDefault, SolidDefaultBlock, SolidDefaultButton, SolidDefaultFull, SolidDefaultRound, SolidDefaultRoundBlock, SolidDefaultRoundButton, SolidDefaultRoundFull, SolidFullButton, SolidLargeButton, SolidLargeRoundButton, SolidSmallButton, SolidSmallRoundButton, StatsCardComponent, StepperComponent, StorageService, SwipeCarouselComponent, TabbedContentComponent, TableSkeletonComponent, TabsComponent, Terminal404Component, TestimonialCardComponent, TestimonialCarouselComponent, TextComponent, TextInputComponent, TextareaInputComponent, ThemeOption, ThemeService, TimelineComponent, TitleBlockComponent, TitleComponent, ToastService, ToggleInputComponent, TokenService, ToolbarActionType, ToolbarComponent, TranslatePipe, TypedCollection, UpdateBannerComponent, UsernameInputComponent, VALTECH_ADS_CONFIG, VALTECH_APP_CONFIG, VALTECH_AUTH_CONFIG, VALTECH_COMPANY_LINKS, VALTECH_DEFAULT_CONTENT, VALTECH_FEEDBACK_CONFIG, VALTECH_FIREBASE_CONFIG, VALTECH_FOOTER_I18N, VALTECH_FOOTER_LOGO, VALTECH_LANGUAGE_SELECTOR, VALTECH_SOCIAL_LINKS, VERSION, WizardComponent, WizardFooterComponent, applyDefaultValueToControl, authGuard, authInterceptor, blogPost, buildFooterLinks, buildPath, collections, createFirebaseConfig, createGlowCardProps, createInitialPaginationState, createNumberFromToField, createTitleProps, docs, extractPathParams, getAppInfo, getAppVersion, getCollectionPath, getDocumentId, getTimeOfDayKey, goToTop, guestGuard, hasEmulators, isAtEnd, isCollectionPath, isDocumentPath, isEmulatorMode, isValidPath, joinPath, maxLength, news, permissionGuard, permissionGuardFromRoute, provideValtechAds, provideValtechAppConfig, provideValtechAuth, provideValtechAuthInterceptor, provideValtechFeedback, provideValtechFirebase, provideValtechI18n, provideValtechPresets, provideValtechSkeleton, query, replaceSpecialChars, resolveColor, resolveInputDefaultValue, roleGuard, storagePaths, superAdminGuard, toArticle };
41631
42073
  //# sourceMappingURL=valtech-components.mjs.map