valtech-components 4.0.177 → 4.0.179

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.
@@ -2,5 +2,5 @@
2
2
  * Current version of valtech-components.
3
3
  * This is automatically updated during the publish process.
4
4
  */
5
- export const VERSION = '4.0.177';
6
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmVyc2lvbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9saWIvdmVyc2lvbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7O0dBR0c7QUFDSCxNQUFNLENBQUMsTUFBTSxPQUFPLEdBQUcsU0FBUyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBDdXJyZW50IHZlcnNpb24gb2YgdmFsdGVjaC1jb21wb25lbnRzLlxuICogVGhpcyBpcyBhdXRvbWF0aWNhbGx5IHVwZGF0ZWQgZHVyaW5nIHRoZSBwdWJsaXNoIHByb2Nlc3MuXG4gKi9cbmV4cG9ydCBjb25zdCBWRVJTSU9OID0gJzQuMC4xNzcnO1xuIl19
5
+ export const VERSION = '4.0.179';
6
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmVyc2lvbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9saWIvdmVyc2lvbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7O0dBR0c7QUFDSCxNQUFNLENBQUMsTUFBTSxPQUFPLEdBQUcsU0FBUyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBDdXJyZW50IHZlcnNpb24gb2YgdmFsdGVjaC1jb21wb25lbnRzLlxuICogVGhpcyBpcyBhdXRvbWF0aWNhbGx5IHVwZGF0ZWQgZHVyaW5nIHRoZSBwdWJsaXNoIHByb2Nlc3MuXG4gKi9cbmV4cG9ydCBjb25zdCBWRVJTSU9OID0gJzQuMC4xNzknO1xuIl19
@@ -1,7 +1,7 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { inject, InjectionToken, makeEnvironmentProviders, APP_INITIALIZER, ErrorHandler, Injectable, DestroyRef, Inject, signal, computed, PLATFORM_ID, isDevMode, Optional, runInInjectionContext, effect, Pipe, TemplateRef, ViewContainerRef, input, Directive, EventEmitter, Component, Input, Output, ChangeDetectionStrategy, HostBinding, HostListener, SecurityContext, NgZone, ViewChild, ElementRef, ChangeDetectorRef, output, ContentChild, model, ViewEncapsulation, untracked, viewChild, Injector, isSignal } from '@angular/core';
3
- import { BehaviorSubject, throwError, Subject, map, distinctUntilChanged, filter as filter$1, take as take$1, firstValueFrom, of, from, EMPTY, Observable, interval, debounceTime, switchMap as switchMap$1, catchError as catchError$1, takeUntil, isObservable, forkJoin, race, timer, shareReplay } from 'rxjs';
4
- import { catchError, switchMap, finalize, filter, take, timeout, map as map$1, tap, distinctUntilChanged as distinctUntilChanged$1, retry, debounceTime as debounceTime$1, takeUntil as takeUntil$1 } from 'rxjs/operators';
3
+ import { BehaviorSubject, throwError, Subject, map, distinctUntilChanged, filter as filter$1, take as take$1, firstValueFrom, of, from, EMPTY, Observable, interval, debounceTime, switchMap as switchMap$1, catchError as catchError$1, takeUntil, isObservable, forkJoin, race, timer, shareReplay as shareReplay$1 } from 'rxjs';
4
+ import { catchError, switchMap, finalize, filter, take, timeout, map as map$1, tap, shareReplay, distinctUntilChanged as distinctUntilChanged$1, retry, debounceTime as debounceTime$1, takeUntil as takeUntil$1 } from 'rxjs/operators';
5
5
  import * as i1$3 from '@angular/common/http';
6
6
  import { provideHttpClient, withInterceptors, HttpClient, HttpErrorResponse } from '@angular/common/http';
7
7
  import * as i1$1 from '@angular/fire/firestore';
@@ -56,7 +56,7 @@ import { BrowserMultiFormatReader } from '@zxing/browser';
56
56
  * Current version of valtech-components.
57
57
  * This is automatically updated during the publish process.
58
58
  */
59
- const VERSION = '4.0.177';
59
+ const VERSION = '4.0.179';
60
60
 
61
61
  // Control de estado de refresco (singleton a nivel de módulo)
62
62
  let isRefreshing = false;
@@ -8352,6 +8352,13 @@ class AuthService {
8352
8352
  this.refreshTimerId = null;
8353
8353
  this.syncSubscription = null;
8354
8354
  this.firestoreRBACUnsubscribe = null;
8355
+ // Single-flight refresh: un solo POST /refresh en vuelo a la vez. Todos los
8356
+ // callers (bootstrap, reestablecer Firebase, interceptor 401, timer proactivo)
8357
+ // comparten el MISMO observable. Sin esto, refreshes concurrentes chocan contra
8358
+ // la rotación de refresh token del backend: el primero rota el token y el
8359
+ // segundo llega con el token ya invalidado → 401 → logout espurio (el usuario
8360
+ // cae a la landing al actualizar). Ver project_pwa-login-freeze.
8361
+ this.inFlightRefresh$ = null;
8355
8362
  // =============================================
8356
8363
  // ESTADO PÚBLICO (Signals readonly)
8357
8364
  // =============================================
@@ -8762,13 +8769,20 @@ class AuthService {
8762
8769
  * que el cliente debe guardar para el próximo refresh.
8763
8770
  */
8764
8771
  refreshAccessToken() {
8772
+ // Single-flight: si ya hay un /refresh en vuelo, devolvemos el MISMO
8773
+ // observable (multicast vía shareReplay). Evita la carrera contra la rotación
8774
+ // de refresh token del backend, que producía 401 espurios → logout al
8775
+ // actualizar. Ver inFlightRefresh$ y project_pwa-login-freeze.
8776
+ if (this.inFlightRefresh$) {
8777
+ return this.inFlightRefresh$;
8778
+ }
8765
8779
  // H-05 same-site-aware: en same-site (prod) el refresh va SOLO por la cookie
8766
8780
  // HttpOnly (body vacío), no se persiste en localStorage → un XSS no lo roba.
8767
8781
  // En cross-site (dev) la cookie de terceros se bloquea (Safari ITP / Chrome
8768
8782
  // 3rd-party phase-out) → se manda en el body + se persiste (fallback).
8769
8783
  // withCredentials adjunta la cookie cuando el browser la permite.
8770
8784
  const body = this.storageService.cookieOnly ? {} : { refreshToken: this.state().refreshToken };
8771
- return this.http
8785
+ this.inFlightRefresh$ = this.http
8772
8786
  .post(`${this.baseUrl}/refresh`, body, { withCredentials: true })
8773
8787
  .pipe(
8774
8788
  // Nunca dejar el refresh colgado: si la red/Lambda no responde a tiempo,
@@ -8798,7 +8812,17 @@ class AuthService {
8798
8812
  // initialize() only clears state (no redirect). Auto-navigating to
8799
8813
  // /login from this method redirects even users on public routes.
8800
8814
  return throwError(() => error);
8801
- }));
8815
+ }),
8816
+ // Liberar el slot al terminar (éxito, error o timeout) para que el
8817
+ // próximo refresh real arranque uno nuevo.
8818
+ finalize(() => {
8819
+ this.inFlightRefresh$ = null;
8820
+ }),
8821
+ // Multicast: los side-effects del tap corren UNA vez y todos los callers
8822
+ // concurrentes reciben la misma respuesta/error. refCount:false → que un
8823
+ // caller se desuscribe temprano NO cancela la rotación de token en vuelo.
8824
+ shareReplay({ bufferSize: 1, refCount: false }));
8825
+ return this.inFlightRefresh$;
8802
8826
  }
8803
8827
  /**
8804
8828
  * Maneja autenticación exitosa desde fuentes externas (OAuth, etc).
@@ -28351,6 +28375,13 @@ class UpdateBannerComponent {
28351
28375
  dismissAction: this.tr('dismissAction'),
28352
28376
  };
28353
28377
  });
28378
+ /**
28379
+ * Feedback visual mientras se aplica el update. El botón pasa a estado
28380
+ * WORKING (spinner) y se bloquean nuevos taps. `applyUpdate()` siempre
28381
+ * recarga la página al terminar (try/finally), así que NO reseteamos este
28382
+ * flag: queda en true hasta que el reload reemplaza la app.
28383
+ */
28384
+ this.applying = signal(false);
28354
28385
  if (!this.i18n.hasNamespace(UPDATE_BANNER_I18N_NAMESPACE)) {
28355
28386
  this.i18n.registerContent(UPDATE_BANNER_I18N_NAMESPACE, {
28356
28387
  es: UPDATE_BANNER_DEFAULT_CONTENT.es,
@@ -28360,6 +28391,11 @@ class UpdateBannerComponent {
28360
28391
  }
28361
28392
  /** Aplica la actualización (activa el SW si existe + recarga). */
28362
28393
  onUpdate() {
28394
+ // Anti multi-tap: el botón WORKING no queda `disabled` a nivel DOM, así que
28395
+ // sin este guard un usuario impaciente dispararía applyUpdate() N veces.
28396
+ if (this.applying())
28397
+ return;
28398
+ this.applying.set(true);
28363
28399
  void this.version.applyUpdate();
28364
28400
  }
28365
28401
  /** Descarta el banner opcional. No-op en modo obligatorio. */
@@ -28429,7 +28465,7 @@ class UpdateBannerComponent {
28429
28465
  color: 'primary',
28430
28466
  fill: 'solid',
28431
28467
  size: 'small',
28432
- state: 'ENABLED',
28468
+ state: applying() ? 'WORKING' : 'ENABLED',
28433
28469
  type: 'button',
28434
28470
  }"
28435
28471
  (onClick)="onUpdate()"
@@ -28441,7 +28477,7 @@ class UpdateBannerComponent {
28441
28477
  color: 'dark',
28442
28478
  fill: 'clear',
28443
28479
  size: 'small',
28444
- state: 'ENABLED',
28480
+ state: applying() ? 'DISABLED' : 'ENABLED',
28445
28481
  type: 'button',
28446
28482
  }"
28447
28483
  (onClick)="onDismiss()"
@@ -28504,7 +28540,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
28504
28540
  color: 'primary',
28505
28541
  fill: 'solid',
28506
28542
  size: 'small',
28507
- state: 'ENABLED',
28543
+ state: applying() ? 'WORKING' : 'ENABLED',
28508
28544
  type: 'button',
28509
28545
  }"
28510
28546
  (onClick)="onUpdate()"
@@ -28516,7 +28552,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
28516
28552
  color: 'dark',
28517
28553
  fill: 'clear',
28518
28554
  size: 'small',
28519
- state: 'ENABLED',
28555
+ state: applying() ? 'DISABLED' : 'ENABLED',
28520
28556
  type: 'button',
28521
28557
  }"
28522
28558
  (onClick)="onDismiss()"
@@ -35347,7 +35383,7 @@ class HtmlViewerModalComponent {
35347
35383
  <ion-content>
35348
35384
  <iframe [srcdoc]="safeHtml" style="width: 100%; height: 100%; border: none;" title="Visor de documento"></iframe>
35349
35385
  </ion-content>
35350
- `, isInline: true, styles: [":host{display:flex;flex-direction:column}ion-content{--padding-bottom: 0;--padding-top: 0}iframe{display:block}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "component", type: IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }] }); }
35386
+ `, isInline: true, styles: [":host{display:flex;flex-direction:column}ion-content{--padding-top: 12px;--padding-bottom: 12px;--padding-start: 12px;--padding-end: 12px;--background: var(--ion-color-light, #f4f5f8)}iframe{display:block;border-radius:8px;background:#fff}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "component", type: IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }] }); }
35351
35387
  }
35352
35388
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: HtmlViewerModalComponent, decorators: [{
35353
35389
  type: Component,
@@ -35370,7 +35406,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
35370
35406
  <ion-content>
35371
35407
  <iframe [srcdoc]="safeHtml" style="width: 100%; height: 100%; border: none;" title="Visor de documento"></iframe>
35372
35408
  </ion-content>
35373
- `, styles: [":host{display:flex;flex-direction:column}ion-content{--padding-bottom: 0;--padding-top: 0}iframe{display:block}\n"] }]
35409
+ `, styles: [":host{display:flex;flex-direction:column}ion-content{--padding-top: 12px;--padding-bottom: 12px;--padding-start: 12px;--padding-end: 12px;--background: var(--ion-color-light, #f4f5f8)}iframe{display:block;border-radius:8px;background:#fff}\n"] }]
35374
35410
  }], propDecorators: { html: [{
35375
35411
  type: Input
35376
35412
  }], title: [{
@@ -54194,7 +54230,8 @@ const ORGANIZATION_VIEW_I18N = {
54194
54230
  apiKeysFeaturePermsDesc: 'Cada key lleva solo los permisos que tú le asignes — concede el mínimo.',
54195
54231
  apiKeysFeatureSecretTitle: 'Secreto único',
54196
54232
  apiKeysFeatureSecretDesc: 'El secreto se muestra una sola vez. Guárdalo en un gestor de secretos.',
54197
- switchOrgCta: 'Cambiar organización',
54233
+ activeOrgTitle: 'Organización activa',
54234
+ switchOrgCta: 'Cambiar',
54198
54235
  orgsMoreInfo: 'Aprender más sobre organizaciones',
54199
54236
  orgsInfoTitle: '¿Qué son las organizaciones?',
54200
54237
  orgsInfoSubtitle: 'Todo lo que necesitas saber',
@@ -54293,7 +54330,8 @@ const ORGANIZATION_VIEW_I18N = {
54293
54330
  apiKeysFeaturePermsDesc: 'Each key carries only the permissions you assign — grant the minimum.',
54294
54331
  apiKeysFeatureSecretTitle: 'One-time secret',
54295
54332
  apiKeysFeatureSecretDesc: 'The secret is shown only once. Store it in a secret manager.',
54296
- switchOrgCta: 'Switch organization',
54333
+ activeOrgTitle: 'Active organization',
54334
+ switchOrgCta: 'Switch',
54297
54335
  orgsMoreInfo: 'Learn more about organizations',
54298
54336
  orgsInfoTitle: 'What are organizations?',
54299
54337
  orgsInfoSubtitle: 'Everything you need to know',
@@ -54485,14 +54523,18 @@ class OrganizationViewComponent {
54485
54523
  return this.tt('planEnterprise');
54486
54524
  return this.tt('planFree');
54487
54525
  });
54488
- this.switchOrgButtonProps = computed(() => ({
54489
- text: this.tt('switchOrgCta'),
54490
- color: 'dark',
54491
- fill: 'outline',
54492
- size: 'default',
54493
- shape: 'round',
54494
- type: 'button',
54495
- token: 'org-switch',
54526
+ this.switchOrgSectionProps = computed(() => ({
54527
+ title: this.tt('activeOrgTitle'),
54528
+ action: {
54529
+ text: this.tt('switchOrgCta'),
54530
+ color: 'dark',
54531
+ fill: 'outline',
54532
+ size: 'small',
54533
+ shape: 'round',
54534
+ type: 'button',
54535
+ token: 'org-switch',
54536
+ state: ComponentStates.ENABLED,
54537
+ },
54496
54538
  }));
54497
54539
  this.editButtonProps = computed(() => ({
54498
54540
  text: this.tt('editCta'),
@@ -55011,15 +55053,8 @@ class OrganizationViewComponent {
55011
55053
  content: pageDescription(),
55012
55054
  }"
55013
55055
  />
55014
- <!-- "Aprender más sobre organizaciones" bajo el título+subtítulo
55015
- principal de la vista (abre el explainer org-info-sheet). -->
55016
- <button class="org-more-info-link" type="button" (click)="onOrgInfo()">
55017
- {{ tt('orgsMoreInfo') }}
55018
- </button>
55019
- <!-- Cambiar de organización activa. -->
55020
- <div class="org-switch-row">
55021
- <val-button [props]="switchOrgButtonProps()" (onClick)="onSwitchOrg()" />
55022
- </div>
55056
+ <!-- Sección "Organización activa" con botón Cambiar a la derecha. -->
55057
+ <val-section-header [props]="switchOrgSectionProps()" (actionClick)="onSwitchOrg()" />
55023
55058
  </header>
55024
55059
 
55025
55060
  @if (resolvedConfig().showInfo) {
@@ -55321,7 +55356,7 @@ class OrganizationViewComponent {
55321
55356
  (created)="onOrgCreated($event)"
55322
55357
  />
55323
55358
  </div>
55324
- `, isInline: true, styles: [".page{padding:16px 0;max-width:720px;margin:0 auto}.org-more-info-link{background:none;border:none;padding:4px 0;margin-top:4px;font-size:13px;font-weight:600;color:var(--ion-color-primary, #7026df);cursor:pointer;text-align:left}.page-header{margin-bottom:16px}.settings-section{padding:16px 0}.settings-section+.settings-section{border-top:1px solid var(--val-border-color, rgba(0, 0, 0, .08))}.section-header-row{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:8px}.section-body{display:flex;flex-direction:column;gap:10px}.row-actions{margin-top:12px}.row-actions--gap{display:flex;gap:8px}.invite-cta{display:flex;flex-direction:column;gap:16px}.invite-cta__text{display:flex;flex-direction:column;gap:4px}.invite-cta__actions{display:flex;gap:12px;flex-wrap:wrap}.org-info-card{border-radius:14px;background:var(--ion-color-light, #f4f5f8);overflow:hidden}.org-info-logo{display:flex;justify-content:center;padding:8px 0 16px}.org-logo-img{width:80px;height:80px;border-radius:50%;object-fit:cover;border:2px solid var(--ion-color-light)}:host-context(body.dark) .org-info-card,:host-context(html.ion-palette-dark) .org-info-card,:host-context([data-theme=\"dark\"]) .org-info-card{background:#ffffff0d}.org-info-field{padding:12px 16px;display:flex;flex-direction:column;gap:4px;border-bottom:1px solid var(--val-border-color, rgba(0, 0, 0, .06))}.org-info-field:last-child{border-bottom:none}.org-info-label{font-size:.74rem;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:var(--ion-color-medium)}.org-info-value{font-size:.95rem;font-weight:500;color:var(--ion-color-dark)}.org-info-value--muted{color:var(--ion-color-medium);font-weight:400}.plan-badge{display:inline-block;font-size:.78rem;font-weight:700;padding:3px 10px;border-radius:20px;width:fit-content}.plan-badge--free{background:var(--ion-color-light-shade, #d7d8da);color:var(--ion-color-dark)}.plan-badge--pro{background:var(--ion-color-primary);color:#fff}.plan-badge--enterprise{background:#f5c542;color:#222}.members-list{display:flex;flex-direction:column;gap:8px}.members-show-more{background:none;border:none;color:var(--ion-color-primary);font-size:14px;font-weight:500;cursor:pointer;padding:8px 0}.rbac-debug{opacity:.7}.rbac-debug__body{display:flex;flex-direction:column;gap:6px;font-family:monospace;font-size:.78rem}.rbac-debug__row{display:flex;gap:8px;align-items:flex-start}.rbac-debug__label{color:var(--ion-color-medium);min-width:72px;flex-shrink:0}.rbac-debug__value{color:var(--ion-color-dark);word-break:break-all}.rbac-debug__value--perms{color:var(--ion-color-medium)}.section-title-danger{display:flex;align-items:center;gap:8px;margin-bottom:12px}.section-title-danger val-title{display:block;margin:0}.danger-icon{display:block;font-size:1.125rem;color:var(--ion-color-warning-shade, #d4a017);flex-shrink:0}\n"], dependencies: [{ kind: "component", type: CreateOrgModalComponent, selector: "val-create-org-modal", inputs: ["i18nNamespace", "isOpen"], outputs: ["dismissed", "created"] }, { kind: "component", type: CtaCardComponent, selector: "val-cta-card", inputs: ["props"], outputs: ["onAction"] }, { kind: "component", type: DisplayComponent, selector: "val-display", inputs: ["props"] }, { kind: "component", type: EmptyStateComponent, selector: "val-empty-state", inputs: ["props"] }, { kind: "directive", type: HasPermissionDirective, selector: "[valHasPermission]", inputs: ["valHasPermission"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: SkeletonLayoutComponent, selector: "val-skeleton-layout", inputs: ["props"] }, { kind: "component", type: TitleComponent, selector: "val-title", inputs: ["props"] }, { kind: "component", type: TextComponent, selector: "val-text", inputs: ["props"] }, { kind: "component", type: ButtonComponent, selector: "val-button", inputs: ["preset", "props"], outputs: ["onClick"] }, { kind: "component", type: MemberCardComponent, selector: "val-member-card", inputs: ["props"], outputs: ["onAction"] }, { kind: "component", type: PermissionsModalComponent, selector: "val-permissions-modal", inputs: ["isOpen", "config"], outputs: ["dismissed"] }] }); }
55359
+ `, isInline: true, styles: [".page{padding:16px 0;max-width:720px;margin:0 auto}.org-more-info-link{background:none;border:none;padding:4px 0;margin-top:4px;font-size:13px;font-weight:600;color:var(--ion-color-primary, #7026df);cursor:pointer;text-align:left}.page-header{margin-bottom:16px}.settings-section{padding:16px 0}.settings-section+.settings-section{border-top:1px solid var(--val-border-color, rgba(0, 0, 0, .08))}.section-header-row{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:8px}.section-body{display:flex;flex-direction:column;gap:10px}.row-actions{margin-top:12px}.row-actions--gap{display:flex;gap:8px}.invite-cta{display:flex;flex-direction:column;gap:16px}.invite-cta__text{display:flex;flex-direction:column;gap:4px}.invite-cta__actions{display:flex;gap:12px;flex-wrap:wrap}.org-info-card{border-radius:14px;background:var(--ion-color-light, #f4f5f8);overflow:hidden}.org-info-logo{display:flex;justify-content:center;padding:8px 0 16px}.org-logo-img{width:80px;height:80px;border-radius:50%;object-fit:cover;border:2px solid var(--ion-color-light)}:host-context(body.dark) .org-info-card,:host-context(html.ion-palette-dark) .org-info-card,:host-context([data-theme=\"dark\"]) .org-info-card{background:#ffffff0d}.org-info-field{padding:12px 16px;display:flex;flex-direction:column;gap:4px;border-bottom:1px solid var(--val-border-color, rgba(0, 0, 0, .06))}.org-info-field:last-child{border-bottom:none}.org-info-label{font-size:.74rem;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:var(--ion-color-medium)}.org-info-value{font-size:.95rem;font-weight:500;color:var(--ion-color-dark)}.org-info-value--muted{color:var(--ion-color-medium);font-weight:400}.plan-badge{display:inline-block;font-size:.78rem;font-weight:700;padding:3px 10px;border-radius:20px;width:fit-content}.plan-badge--free{background:var(--ion-color-light-shade, #d7d8da);color:var(--ion-color-dark)}.plan-badge--pro{background:var(--ion-color-primary);color:#fff}.plan-badge--enterprise{background:#f5c542;color:#222}.members-list{display:flex;flex-direction:column;gap:8px}.members-show-more{background:none;border:none;color:var(--ion-color-primary);font-size:14px;font-weight:500;cursor:pointer;padding:8px 0}.rbac-debug{opacity:.7}.rbac-debug__body{display:flex;flex-direction:column;gap:6px;font-family:monospace;font-size:.78rem}.rbac-debug__row{display:flex;gap:8px;align-items:flex-start}.rbac-debug__label{color:var(--ion-color-medium);min-width:72px;flex-shrink:0}.rbac-debug__value{color:var(--ion-color-dark);word-break:break-all}.rbac-debug__value--perms{color:var(--ion-color-medium)}.section-title-danger{display:flex;align-items:center;gap:8px;margin-bottom:12px}.section-title-danger val-title{display:block;margin:0}.danger-icon{display:block;font-size:1.125rem;color:var(--ion-color-warning-shade, #d4a017);flex-shrink:0}\n"], dependencies: [{ kind: "component", type: CreateOrgModalComponent, selector: "val-create-org-modal", inputs: ["i18nNamespace", "isOpen"], outputs: ["dismissed", "created"] }, { kind: "component", type: CtaCardComponent, selector: "val-cta-card", inputs: ["props"], outputs: ["onAction"] }, { kind: "component", type: DisplayComponent, selector: "val-display", inputs: ["props"] }, { kind: "component", type: EmptyStateComponent, selector: "val-empty-state", inputs: ["props"] }, { kind: "directive", type: HasPermissionDirective, selector: "[valHasPermission]", inputs: ["valHasPermission"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: SkeletonLayoutComponent, selector: "val-skeleton-layout", inputs: ["props"] }, { kind: "component", type: TitleComponent, selector: "val-title", inputs: ["props"] }, { kind: "component", type: TextComponent, selector: "val-text", inputs: ["props"] }, { kind: "component", type: ButtonComponent, selector: "val-button", inputs: ["preset", "props"], outputs: ["onClick"] }, { kind: "component", type: MemberCardComponent, selector: "val-member-card", inputs: ["props"], outputs: ["onAction"] }, { kind: "component", type: PermissionsModalComponent, selector: "val-permissions-modal", inputs: ["isOpen", "config"], outputs: ["dismissed"] }, { kind: "component", type: SectionHeaderComponent, selector: "val-section-header", inputs: ["props"], outputs: ["actionClick"] }] }); }
55325
55360
  }
55326
55361
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: OrganizationViewComponent, decorators: [{
55327
55362
  type: Component,
@@ -55341,6 +55376,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
55341
55376
  TransferOwnershipModalComponent,
55342
55377
  MemberImportModalComponent,
55343
55378
  OrgInfoSheetComponent,
55379
+ SectionHeaderComponent,
55344
55380
  SwitchOrgModalComponent,
55345
55381
  ], template: `
55346
55382
  <div class="page">
@@ -55354,15 +55390,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
55354
55390
  content: pageDescription(),
55355
55391
  }"
55356
55392
  />
55357
- <!-- "Aprender más sobre organizaciones" bajo el título+subtítulo
55358
- principal de la vista (abre el explainer org-info-sheet). -->
55359
- <button class="org-more-info-link" type="button" (click)="onOrgInfo()">
55360
- {{ tt('orgsMoreInfo') }}
55361
- </button>
55362
- <!-- Cambiar de organización activa. -->
55363
- <div class="org-switch-row">
55364
- <val-button [props]="switchOrgButtonProps()" (onClick)="onSwitchOrg()" />
55365
- </div>
55393
+ <!-- Sección "Organización activa" con botón Cambiar a la derecha. -->
55394
+ <val-section-header [props]="switchOrgSectionProps()" (actionClick)="onSwitchOrg()" />
55366
55395
  </header>
55367
55396
 
55368
55397
  @if (resolvedConfig().showInfo) {
@@ -61903,7 +61932,7 @@ class LegalContentService {
61903
61932
  const stream = fallback && fallback !== locale
61904
61933
  ? primary.pipe(catchError$1(() => this.loadOne(slug, fallback, options.basePath)))
61905
61934
  : primary;
61906
- const shared = stream.pipe(shareReplay({ bufferSize: 1, refCount: false }));
61935
+ const shared = stream.pipe(shareReplay$1({ bufferSize: 1, refCount: false }));
61907
61936
  this.cache.set(key, shared);
61908
61937
  return shared;
61909
61938
  }