valtech-components 2.0.989 → 2.0.990

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.
@@ -54,7 +54,7 @@ import 'prismjs/components/prism-json';
54
54
  * Current version of valtech-components.
55
55
  * This is automatically updated during the publish process.
56
56
  */
57
- const VERSION = '2.0.989';
57
+ const VERSION = '2.0.990';
58
58
 
59
59
  // Control de estado de refresco (singleton a nivel de módulo)
60
60
  let isRefreshing = false;
@@ -41737,7 +41737,7 @@ const PROFILE_VIEW_I18N = {
41737
41737
  const HANDLE_PATTERN = /^[a-z_][a-z0-9_]*$/;
41738
41738
  const HANDLE_MIN = 3;
41739
41739
  const HANDLE_MAX = 20;
41740
- const DEFAULT_NAMESPACE$b = 'Settings.Profile';
41740
+ const DEFAULT_NAMESPACE$c = 'Settings.Profile';
41741
41741
  /**
41742
41742
  * `val-profile-view` — vista Perfil full-feature autocontenida (organism).
41743
41743
  *
@@ -41787,7 +41787,7 @@ class ProfileViewComponent {
41787
41787
  showPhone: merged.showPhone ?? true,
41788
41788
  showShareCta: merged.showShareCta ?? true,
41789
41789
  profileBaseUrl: merged.profileBaseUrl ?? '',
41790
- i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$b,
41790
+ i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$c,
41791
41791
  onSaved: merged.onSaved,
41792
41792
  onAvatarUploaded: merged.onAvatarUploaded,
41793
41793
  };
@@ -42703,7 +42703,7 @@ const PREFERENCES_VIEW_I18N = {
42703
42703
  },
42704
42704
  };
42705
42705
 
42706
- const DEFAULT_NAMESPACE$a = 'Settings.General';
42706
+ const DEFAULT_NAMESPACE$b = 'Settings.General';
42707
42707
  const DEFAULT_LANGUAGES = ['es', 'en'];
42708
42708
  /**
42709
42709
  * `val-preferences-view` — vista de preferencias de UI cross-app (tema +
@@ -42740,7 +42740,7 @@ class PreferencesViewComponent {
42740
42740
  showLanguage: merged.showLanguage ?? true,
42741
42741
  showFontSize: merged.showFontSize ?? true,
42742
42742
  supportedLanguages: merged.supportedLanguages ?? DEFAULT_LANGUAGES,
42743
- i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$a,
42743
+ i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$b,
42744
42744
  onThemeChanged: merged.onThemeChanged,
42745
42745
  onLanguageChanged: merged.onLanguageChanged,
42746
42746
  onFontSizeChanged: merged.onFontSizeChanged,
@@ -44552,7 +44552,7 @@ const SECURITY_VIEW_I18N = {
44552
44552
  };
44553
44553
 
44554
44554
  addIcons({ laptopOutline, phonePortraitOutline });
44555
- const DEFAULT_NAMESPACE$9 = 'Settings.Security';
44555
+ const DEFAULT_NAMESPACE$a = 'Settings.Security';
44556
44556
  /**
44557
44557
  * `val-security-view` — vista Seguridad full-feature autocontenida (organism).
44558
44558
  *
@@ -44601,7 +44601,7 @@ class SecurityViewComponent {
44601
44601
  showChangeEmail: merged.showChangeEmail ?? true,
44602
44602
  showMfa: merged.showMfa ?? true,
44603
44603
  showPolicies: merged.showPolicies ?? true,
44604
- i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$9,
44604
+ i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$a,
44605
44605
  handleDeepLinks: merged.handleDeepLinks ?? true,
44606
44606
  homeRoute: merged.homeRoute ?? '/app/home',
44607
44607
  onMfaCompleted: merged.onMfaCompleted,
@@ -45507,7 +45507,7 @@ const CREATE_ORG_MODAL_I18N = {
45507
45507
  },
45508
45508
  };
45509
45509
 
45510
- const DEFAULT_NAMESPACE$8 = 'CreateOrgModal';
45510
+ const DEFAULT_NAMESPACE$9 = 'CreateOrgModal';
45511
45511
  /**
45512
45512
  * `val-create-org-modal` — modal de creación de organización (organism
45513
45513
  * compartido). Promovido desde `showcase` bajo el proceso de ADR-021. Reusado por
@@ -45533,10 +45533,10 @@ class CreateOrgModalComponent {
45533
45533
  this.toast = inject(ToastService);
45534
45534
  this.errors = inject(ValtechErrorService);
45535
45535
  /** Namespace i18n con que la vista resuelve sus textos. */
45536
- this.i18nNamespace = DEFAULT_NAMESPACE$8;
45536
+ this.i18nNamespace = DEFAULT_NAMESPACE$9;
45537
45537
  this._busy = signal(false);
45538
- if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$8)) {
45539
- this.i18n.registerContent(DEFAULT_NAMESPACE$8, CREATE_ORG_MODAL_I18N);
45538
+ if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$9)) {
45539
+ this.i18n.registerContent(DEFAULT_NAMESPACE$9, CREATE_ORG_MODAL_I18N);
45540
45540
  }
45541
45541
  this.formMeta = signal(this.buildFormMeta());
45542
45542
  effect(() => {
@@ -45772,7 +45772,7 @@ const DELETE_ACCOUNT_MODAL_I18N = {
45772
45772
  },
45773
45773
  };
45774
45774
 
45775
- const DEFAULT_NAMESPACE$7 = 'Settings.DeleteAccount';
45775
+ const DEFAULT_NAMESPACE$8 = 'Settings.DeleteAccount';
45776
45776
  /**
45777
45777
  * `val-delete-account-modal` — modal de eliminación de cuenta (organism,
45778
45778
  * cuenta-específico). Promovido desde `showcase` bajo el proceso de ADR-021.
@@ -45798,7 +45798,7 @@ class DeleteAccountModalComponent {
45798
45798
  this.i18n = inject(I18nService);
45799
45799
  this.toast = inject(ToastService);
45800
45800
  /** Namespace i18n con que la vista resuelve sus textos. */
45801
- this.i18nNamespace = DEFAULT_NAMESPACE$7;
45801
+ this.i18nNamespace = DEFAULT_NAMESPACE$8;
45802
45802
  this.hasPassword = signal(null);
45803
45803
  this.codeSent = signal(false);
45804
45804
  this.formState = signal(ComponentStates.ENABLED);
@@ -45865,8 +45865,8 @@ class DeleteAccountModalComponent {
45865
45865
  ],
45866
45866
  actions: this.submitBtn('delete-code-submit', 'confirmCta'),
45867
45867
  }));
45868
- if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$7)) {
45869
- this.i18n.registerContent(DEFAULT_NAMESPACE$7, DELETE_ACCOUNT_MODAL_I18N);
45868
+ if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$8)) {
45869
+ this.i18n.registerContent(DEFAULT_NAMESPACE$8, DELETE_ACCOUNT_MODAL_I18N);
45870
45870
  }
45871
45871
  }
45872
45872
  ngOnInit() {
@@ -46067,7 +46067,7 @@ const ORG_INFO_SHEET_I18N = {
46067
46067
  },
46068
46068
  };
46069
46069
 
46070
- const DEFAULT_NAMESPACE$6 = 'OrgInfoSheet';
46070
+ const DEFAULT_NAMESPACE$7 = 'OrgInfoSheet';
46071
46071
  /**
46072
46072
  * `val-org-info-sheet` — sheet informativo "¿Qué son las organizaciones?".
46073
46073
  * Extraído del `OrgInfoSheetComponent` inline de `AccountPage` bajo el proceso de
@@ -46112,15 +46112,15 @@ class OrgInfoSheetComponent {
46112
46112
  text: this.closeProps.text || this.resolvedCloseLabel(),
46113
46113
  }));
46114
46114
  addIcons({ peopleOutline, swapHorizontalOutline, mailOutline });
46115
- if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$6)) {
46116
- this.i18n.registerContent(DEFAULT_NAMESPACE$6, ORG_INFO_SHEET_I18N);
46115
+ if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$7)) {
46116
+ this.i18n.registerContent(DEFAULT_NAMESPACE$7, ORG_INFO_SHEET_I18N);
46117
46117
  }
46118
46118
  }
46119
46119
  dismiss() {
46120
46120
  this._modalRef?.dismiss(null, 'cancel');
46121
46121
  }
46122
46122
  tt(key) {
46123
- return this.i18n.t(key, DEFAULT_NAMESPACE$6);
46123
+ return this.i18n.t(key, DEFAULT_NAMESPACE$7);
46124
46124
  }
46125
46125
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: OrgInfoSheetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
46126
46126
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: OrgInfoSheetComponent, isStandalone: true, selector: "val-org-info-sheet", inputs: { title: "title", subtitle: "subtitle", body: "body", closeLabel: "closeLabel", closeProps: "closeProps", _modalRef: "_modalRef" }, ngImport: i0, template: `
@@ -46287,7 +46287,7 @@ const SWITCH_ORG_MODAL_I18N = {
46287
46287
  },
46288
46288
  };
46289
46289
 
46290
- const DEFAULT_NAMESPACE$5 = 'Settings.SwitchOrg';
46290
+ const DEFAULT_NAMESPACE$6 = 'Settings.SwitchOrg';
46291
46291
  addIcons({ businessOutline, checkmarkCircleOutline });
46292
46292
  /**
46293
46293
  * `val-switch-org-modal` — modal de cambio de organización activa (organism
@@ -46313,7 +46313,7 @@ class SwitchOrgModalComponent {
46313
46313
  this.orgSwitch = inject(OrgSwitchService);
46314
46314
  this.errors = inject(ValtechErrorService);
46315
46315
  /** Namespace i18n con que la vista resuelve sus textos. */
46316
- this.i18nNamespace = DEFAULT_NAMESPACE$5;
46316
+ this.i18nNamespace = DEFAULT_NAMESPACE$6;
46317
46317
  this.orgs = signal([]);
46318
46318
  this.loading = signal(false);
46319
46319
  this.query = signal('');
@@ -46329,8 +46329,8 @@ class SwitchOrgModalComponent {
46329
46329
  return list;
46330
46330
  return list.filter(o => o.name.toLowerCase().includes(q));
46331
46331
  });
46332
- if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$5)) {
46333
- this.i18n.registerContent(DEFAULT_NAMESPACE$5, SWITCH_ORG_MODAL_I18N);
46332
+ if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$6)) {
46333
+ this.i18n.registerContent(DEFAULT_NAMESPACE$6, SWITCH_ORG_MODAL_I18N);
46334
46334
  }
46335
46335
  this.loadOrgs();
46336
46336
  }
@@ -46648,7 +46648,7 @@ const ACCOUNT_VIEW_I18N = {
46648
46648
  };
46649
46649
 
46650
46650
  addIcons({ peopleOutline, businessOutline, chevronForwardOutline, swapHorizontalOutline, mailOutline });
46651
- const DEFAULT_NAMESPACE$4 = 'Settings.Account';
46651
+ const DEFAULT_NAMESPACE$5 = 'Settings.Account';
46652
46652
  const DEFAULT_MANAGE_ORG_ROUTE = '/app/settings/organization';
46653
46653
  /**
46654
46654
  * `val-account-view` — vista Cuenta full-feature autocontenida (organism).
@@ -46689,7 +46689,7 @@ class AccountViewComponent {
46689
46689
  showNewOrgCta: merged.showNewOrgCta ?? true,
46690
46690
  showLogout: merged.showLogout ?? true,
46691
46691
  showDeleteAccount: merged.showDeleteAccount ?? true,
46692
- i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$4,
46692
+ i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$5,
46693
46693
  manageOrgRoute: merged.manageOrgRoute ?? DEFAULT_MANAGE_ORG_ROUTE,
46694
46694
  onOrgSwitched: merged.onOrgSwitched,
46695
46695
  onOrgCreated: merged.onOrgCreated,
@@ -47376,6 +47376,258 @@ function provideValtechAccountRoutes(opts) {
47376
47376
  ];
47377
47377
  }
47378
47378
 
47379
+ /**
47380
+ * Defaults i18n (es/en) embebidos en `val-edit-org-modal`. Auto-registrados en
47381
+ * el constructor del componente si el consumer no proveyó el namespace
47382
+ * (`EditOrgModal` por default). SOLO incluye las keys que el modal usa.
47383
+ */
47384
+ const EDIT_ORG_MODAL_I18N = {
47385
+ es: {
47386
+ title: 'Editar organización',
47387
+ subtitle: 'Actualiza el nombre y descripción.',
47388
+ nameLabel: 'Nombre',
47389
+ namePlaceholder: 'Nombre de la organización',
47390
+ nameRequired: 'El nombre es obligatorio.',
47391
+ nameMin: 'Mínimo 2 caracteres.',
47392
+ descriptionLabel: 'Descripción (opcional)',
47393
+ descriptionPlaceholder: 'Describe tu organización',
47394
+ saveCta: 'Guardar cambios',
47395
+ close: 'Cancelar',
47396
+ saveSuccess: 'Organización actualizada.',
47397
+ saveError: 'No se pudo guardar. Intenta de nuevo.',
47398
+ fillRequired: 'Completa todos los campos requeridos.',
47399
+ },
47400
+ en: {
47401
+ title: 'Edit organization',
47402
+ subtitle: 'Update the name and description.',
47403
+ nameLabel: 'Name',
47404
+ namePlaceholder: 'Organization name',
47405
+ nameRequired: 'Name is required.',
47406
+ nameMin: 'Minimum 2 characters.',
47407
+ descriptionLabel: 'Description (optional)',
47408
+ descriptionPlaceholder: 'Describe your organization',
47409
+ saveCta: 'Save changes',
47410
+ close: 'Cancel',
47411
+ saveSuccess: 'Organization updated.',
47412
+ saveError: 'Could not save. Please try again.',
47413
+ fillRequired: 'Please fill in all required fields.',
47414
+ },
47415
+ };
47416
+
47417
+ const DEFAULT_NAMESPACE$4 = 'EditOrgModal';
47418
+ /**
47419
+ * `val-edit-org-modal` — modal de edición de organización (nombre + descripción).
47420
+ * Promovido desde `showcase` bajo el proceso de ADR-021. Reusado por la vista de
47421
+ * gestión de organización (y futuras vistas org-aware).
47422
+ *
47423
+ * Edita la org vía `OrgService.updateOrg`. El formulario se construye una vez y se
47424
+ * mantiene reactivo a idioma + estado de trabajo vía effects `allowSignalWrites`.
47425
+ * Si el consumer no pasa `org`, cae al `activeOrgId` del `AuthService`.
47426
+ *
47427
+ * Abierto vía `ModalService` — recibe `_modalRef` por `componentProps` + callback
47428
+ * `onSuccess(updated)`. Header canónico (Regla #5): sin `ion-title`, botón Cerrar
47429
+ * texto en `slot=end`, título/subtítulo en el body con `val-display`/`val-title`.
47430
+ *
47431
+ * Auto-registra sus defaults i18n (es/en) si el consumer no proveyó el namespace
47432
+ * `EditOrgModal`.
47433
+ */
47434
+ class EditOrgModalComponent {
47435
+ constructor() {
47436
+ this.i18n = inject(I18nService);
47437
+ this.orgService = inject(OrgService);
47438
+ this.toast = inject(ToastService);
47439
+ this.errors = inject(ValtechErrorService);
47440
+ this.auth = inject(AuthService);
47441
+ /** Organización a editar (pasada por `componentProps`). */
47442
+ this.org = null;
47443
+ /** Namespace i18n con que la vista resuelve sus textos. */
47444
+ this.i18nNamespace = DEFAULT_NAMESPACE$4;
47445
+ this.saving = signal(false);
47446
+ if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$4)) {
47447
+ this.i18n.registerContent(DEFAULT_NAMESPACE$4, EDIT_ORG_MODAL_I18N);
47448
+ }
47449
+ this.formMeta = signal(this.buildFormMeta());
47450
+ effect(() => {
47451
+ this.i18n.lang();
47452
+ this.formMeta.set(this.buildFormMeta());
47453
+ }, { allowSignalWrites: true });
47454
+ effect(() => {
47455
+ const w = this.saving();
47456
+ this.formMeta.update(m => ({
47457
+ ...m,
47458
+ state: w ? ComponentStates.WORKING : ComponentStates.ENABLED,
47459
+ actions: {
47460
+ ...m.actions,
47461
+ state: w ? ComponentStates.WORKING : ComponentStates.ENABLED,
47462
+ },
47463
+ }));
47464
+ }, { allowSignalWrites: true });
47465
+ }
47466
+ ngOnInit() {
47467
+ this.formMeta.set(this.buildFormMeta());
47468
+ }
47469
+ get activeOrgId() {
47470
+ const u = this.auth.user();
47471
+ return u?.activeOrgId ?? u?.activeOrg ?? '';
47472
+ }
47473
+ buildFormMeta() {
47474
+ const nameField = {
47475
+ token: 'edit-org-name',
47476
+ name: 'name',
47477
+ label: this.t('nameLabel'),
47478
+ hint: '',
47479
+ placeholder: this.t('namePlaceholder'),
47480
+ type: InputType.TEXT,
47481
+ order: 1,
47482
+ validators: [Validators.required, Validators.minLength(2), Validators.maxLength(100)],
47483
+ errors: {
47484
+ required: this.t('nameRequired'),
47485
+ minlength: this.t('nameMin'),
47486
+ },
47487
+ value: this.org?.name ?? '',
47488
+ state: ComponentStates.ENABLED,
47489
+ };
47490
+ const descField = {
47491
+ token: 'edit-org-desc',
47492
+ name: 'description',
47493
+ label: this.t('descriptionLabel'),
47494
+ hint: '',
47495
+ placeholder: this.t('descriptionPlaceholder'),
47496
+ type: InputType.TEXTAREA,
47497
+ order: 2,
47498
+ validators: [],
47499
+ errors: {},
47500
+ value: this.org?.description ?? '',
47501
+ state: ComponentStates.ENABLED,
47502
+ };
47503
+ return {
47504
+ name: '',
47505
+ state: ComponentStates.ENABLED,
47506
+ sections: [{ name: '', order: 1, fields: [nameField, descField] }],
47507
+ actions: {
47508
+ text: this.t('saveCta'),
47509
+ color: 'dark',
47510
+ type: 'submit',
47511
+ fill: 'solid',
47512
+ size: 'default',
47513
+ shape: 'round',
47514
+ expand: 'block',
47515
+ state: ComponentStates.ENABLED,
47516
+ token: 'edit-org-submit',
47517
+ },
47518
+ };
47519
+ }
47520
+ onFormSubmit(event) {
47521
+ const name = event.fields['name'];
47522
+ if (!name?.trim()) {
47523
+ this.toast.show({
47524
+ message: this.t('fillRequired'),
47525
+ duration: 3000,
47526
+ color: 'dark',
47527
+ position: 'top',
47528
+ });
47529
+ return;
47530
+ }
47531
+ const description = event.fields['description'] ?? '';
47532
+ void this.save(name, description);
47533
+ }
47534
+ async save(name, description) {
47535
+ const orgId = this.org?.id ?? this.activeOrgId;
47536
+ if (!orgId || this.saving())
47537
+ return;
47538
+ this.saving.set(true);
47539
+ try {
47540
+ const updated = await firstValueFrom(this.orgService.updateOrg(orgId, { name, description }));
47541
+ this.toast.show({
47542
+ message: this.t('saveSuccess'),
47543
+ color: 'dark',
47544
+ duration: 3500,
47545
+ });
47546
+ this.onSuccess?.(updated);
47547
+ this._modalRef?.dismiss(updated, 'confirm');
47548
+ }
47549
+ catch (err) {
47550
+ this.errors.handle(err, {
47551
+ context: 'editOrgModal.save',
47552
+ fallbackKey: 'saveError',
47553
+ i18nNamespace: this.i18nNamespace,
47554
+ });
47555
+ }
47556
+ finally {
47557
+ this.saving.set(false);
47558
+ }
47559
+ }
47560
+ dismiss() {
47561
+ this._modalRef?.dismiss(null, 'cancel');
47562
+ }
47563
+ t(key) {
47564
+ return this.i18n.t(key, this.i18nNamespace);
47565
+ }
47566
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: EditOrgModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
47567
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: EditOrgModalComponent, isStandalone: true, selector: "val-edit-org-modal", inputs: { _modalRef: "_modalRef", org: "org", onSuccess: "onSuccess", i18nNamespace: "i18nNamespace" }, ngImport: i0, template: `
47568
+ <ion-header>
47569
+ <ion-toolbar>
47570
+ <ion-buttons slot="end">
47571
+ <ion-button fill="clear" color="dark" (click)="dismiss()">
47572
+ <strong>{{ t('close') }}</strong>
47573
+ </ion-button>
47574
+ </ion-buttons>
47575
+ </ion-toolbar>
47576
+ </ion-header>
47577
+ <ion-content class="ion-padding">
47578
+ <val-display [props]="{ content: t('title'), size: 'small', color: 'dark' }" />
47579
+ <val-title
47580
+ [props]="{
47581
+ content: t('subtitle'),
47582
+ size: 'large',
47583
+ color: '',
47584
+ bold: false,
47585
+ }"
47586
+ />
47587
+ <val-form [props]="formMeta()" (onSubmit)="onFormSubmit($event)" />
47588
+ </ion-content>
47589
+ `, isInline: true, dependencies: [{ 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: 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: IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: FormComponent, selector: "val-form", inputs: ["props"], outputs: ["onSubmit", "onInvalid", "onSelectChange"] }, { kind: "component", type: DisplayComponent, selector: "val-display", inputs: ["props"] }, { kind: "component", type: TitleComponent, selector: "val-title", inputs: ["props"] }] }); }
47590
+ }
47591
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: EditOrgModalComponent, decorators: [{
47592
+ type: Component,
47593
+ args: [{
47594
+ selector: 'val-edit-org-modal',
47595
+ standalone: true,
47596
+ imports: [IonHeader, IonToolbar, IonButtons, IonButton, IonContent, FormComponent, DisplayComponent, TitleComponent],
47597
+ template: `
47598
+ <ion-header>
47599
+ <ion-toolbar>
47600
+ <ion-buttons slot="end">
47601
+ <ion-button fill="clear" color="dark" (click)="dismiss()">
47602
+ <strong>{{ t('close') }}</strong>
47603
+ </ion-button>
47604
+ </ion-buttons>
47605
+ </ion-toolbar>
47606
+ </ion-header>
47607
+ <ion-content class="ion-padding">
47608
+ <val-display [props]="{ content: t('title'), size: 'small', color: 'dark' }" />
47609
+ <val-title
47610
+ [props]="{
47611
+ content: t('subtitle'),
47612
+ size: 'large',
47613
+ color: '',
47614
+ bold: false,
47615
+ }"
47616
+ />
47617
+ <val-form [props]="formMeta()" (onSubmit)="onFormSubmit($event)" />
47618
+ </ion-content>
47619
+ `,
47620
+ }]
47621
+ }], ctorParameters: () => [], propDecorators: { _modalRef: [{
47622
+ type: Input
47623
+ }], org: [{
47624
+ type: Input
47625
+ }], onSuccess: [{
47626
+ type: Input
47627
+ }], i18nNamespace: [{
47628
+ type: Input
47629
+ }] } });
47630
+
47379
47631
  /**
47380
47632
  * Defaults i18n (es/en) embebidos en `val-invite-member-modal`. Auto-registrados
47381
47633
  * en el constructor del componente si el consumer no proveyó el namespace
@@ -48386,258 +48638,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
48386
48638
  type: Input
48387
48639
  }] } });
48388
48640
 
48389
- /**
48390
- * Defaults i18n (es/en) embebidos en `val-edit-org-modal`. Auto-registrados en
48391
- * el constructor del componente si el consumer no proveyó el namespace
48392
- * (`EditOrgModal` por default). SOLO incluye las keys que el modal usa.
48393
- */
48394
- const EDIT_ORG_MODAL_I18N = {
48395
- es: {
48396
- title: 'Editar organización',
48397
- subtitle: 'Actualiza el nombre y descripción.',
48398
- nameLabel: 'Nombre',
48399
- namePlaceholder: 'Nombre de la organización',
48400
- nameRequired: 'El nombre es obligatorio.',
48401
- nameMin: 'Mínimo 2 caracteres.',
48402
- descriptionLabel: 'Descripción (opcional)',
48403
- descriptionPlaceholder: 'Describe tu organización',
48404
- saveCta: 'Guardar cambios',
48405
- close: 'Cancelar',
48406
- saveSuccess: 'Organización actualizada.',
48407
- saveError: 'No se pudo guardar. Intenta de nuevo.',
48408
- fillRequired: 'Completa todos los campos requeridos.',
48409
- },
48410
- en: {
48411
- title: 'Edit organization',
48412
- subtitle: 'Update the name and description.',
48413
- nameLabel: 'Name',
48414
- namePlaceholder: 'Organization name',
48415
- nameRequired: 'Name is required.',
48416
- nameMin: 'Minimum 2 characters.',
48417
- descriptionLabel: 'Description (optional)',
48418
- descriptionPlaceholder: 'Describe your organization',
48419
- saveCta: 'Save changes',
48420
- close: 'Cancel',
48421
- saveSuccess: 'Organization updated.',
48422
- saveError: 'Could not save. Please try again.',
48423
- fillRequired: 'Please fill in all required fields.',
48424
- },
48425
- };
48426
-
48427
- const DEFAULT_NAMESPACE$1 = 'EditOrgModal';
48428
- /**
48429
- * `val-edit-org-modal` — modal de edición de organización (nombre + descripción).
48430
- * Promovido desde `showcase` bajo el proceso de ADR-021. Reusado por la vista de
48431
- * gestión de organización (y futuras vistas org-aware).
48432
- *
48433
- * Edita la org vía `OrgService.updateOrg`. El formulario se construye una vez y se
48434
- * mantiene reactivo a idioma + estado de trabajo vía effects `allowSignalWrites`.
48435
- * Si el consumer no pasa `org`, cae al `activeOrgId` del `AuthService`.
48436
- *
48437
- * Abierto vía `ModalService` — recibe `_modalRef` por `componentProps` + callback
48438
- * `onSuccess(updated)`. Header canónico (Regla #5): sin `ion-title`, botón Cerrar
48439
- * texto en `slot=end`, título/subtítulo en el body con `val-display`/`val-title`.
48440
- *
48441
- * Auto-registra sus defaults i18n (es/en) si el consumer no proveyó el namespace
48442
- * `EditOrgModal`.
48443
- */
48444
- class EditOrgModalComponent {
48445
- constructor() {
48446
- this.i18n = inject(I18nService);
48447
- this.orgService = inject(OrgService);
48448
- this.toast = inject(ToastService);
48449
- this.errors = inject(ValtechErrorService);
48450
- this.auth = inject(AuthService);
48451
- /** Organización a editar (pasada por `componentProps`). */
48452
- this.org = null;
48453
- /** Namespace i18n con que la vista resuelve sus textos. */
48454
- this.i18nNamespace = DEFAULT_NAMESPACE$1;
48455
- this.saving = signal(false);
48456
- if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$1)) {
48457
- this.i18n.registerContent(DEFAULT_NAMESPACE$1, EDIT_ORG_MODAL_I18N);
48458
- }
48459
- this.formMeta = signal(this.buildFormMeta());
48460
- effect(() => {
48461
- this.i18n.lang();
48462
- this.formMeta.set(this.buildFormMeta());
48463
- }, { allowSignalWrites: true });
48464
- effect(() => {
48465
- const w = this.saving();
48466
- this.formMeta.update(m => ({
48467
- ...m,
48468
- state: w ? ComponentStates.WORKING : ComponentStates.ENABLED,
48469
- actions: {
48470
- ...m.actions,
48471
- state: w ? ComponentStates.WORKING : ComponentStates.ENABLED,
48472
- },
48473
- }));
48474
- }, { allowSignalWrites: true });
48475
- }
48476
- ngOnInit() {
48477
- this.formMeta.set(this.buildFormMeta());
48478
- }
48479
- get activeOrgId() {
48480
- const u = this.auth.user();
48481
- return u?.activeOrgId ?? u?.activeOrg ?? '';
48482
- }
48483
- buildFormMeta() {
48484
- const nameField = {
48485
- token: 'edit-org-name',
48486
- name: 'name',
48487
- label: this.t('nameLabel'),
48488
- hint: '',
48489
- placeholder: this.t('namePlaceholder'),
48490
- type: InputType.TEXT,
48491
- order: 1,
48492
- validators: [Validators.required, Validators.minLength(2), Validators.maxLength(100)],
48493
- errors: {
48494
- required: this.t('nameRequired'),
48495
- minlength: this.t('nameMin'),
48496
- },
48497
- value: this.org?.name ?? '',
48498
- state: ComponentStates.ENABLED,
48499
- };
48500
- const descField = {
48501
- token: 'edit-org-desc',
48502
- name: 'description',
48503
- label: this.t('descriptionLabel'),
48504
- hint: '',
48505
- placeholder: this.t('descriptionPlaceholder'),
48506
- type: InputType.TEXTAREA,
48507
- order: 2,
48508
- validators: [],
48509
- errors: {},
48510
- value: this.org?.description ?? '',
48511
- state: ComponentStates.ENABLED,
48512
- };
48513
- return {
48514
- name: '',
48515
- state: ComponentStates.ENABLED,
48516
- sections: [{ name: '', order: 1, fields: [nameField, descField] }],
48517
- actions: {
48518
- text: this.t('saveCta'),
48519
- color: 'dark',
48520
- type: 'submit',
48521
- fill: 'solid',
48522
- size: 'default',
48523
- shape: 'round',
48524
- expand: 'block',
48525
- state: ComponentStates.ENABLED,
48526
- token: 'edit-org-submit',
48527
- },
48528
- };
48529
- }
48530
- onFormSubmit(event) {
48531
- const name = event.fields['name'];
48532
- if (!name?.trim()) {
48533
- this.toast.show({
48534
- message: this.t('fillRequired'),
48535
- duration: 3000,
48536
- color: 'dark',
48537
- position: 'top',
48538
- });
48539
- return;
48540
- }
48541
- const description = event.fields['description'] ?? '';
48542
- void this.save(name, description);
48543
- }
48544
- async save(name, description) {
48545
- const orgId = this.org?.id ?? this.activeOrgId;
48546
- if (!orgId || this.saving())
48547
- return;
48548
- this.saving.set(true);
48549
- try {
48550
- const updated = await firstValueFrom(this.orgService.updateOrg(orgId, { name, description }));
48551
- this.toast.show({
48552
- message: this.t('saveSuccess'),
48553
- color: 'dark',
48554
- duration: 3500,
48555
- });
48556
- this.onSuccess?.(updated);
48557
- this._modalRef?.dismiss(updated, 'confirm');
48558
- }
48559
- catch (err) {
48560
- this.errors.handle(err, {
48561
- context: 'editOrgModal.save',
48562
- fallbackKey: 'saveError',
48563
- i18nNamespace: this.i18nNamespace,
48564
- });
48565
- }
48566
- finally {
48567
- this.saving.set(false);
48568
- }
48569
- }
48570
- dismiss() {
48571
- this._modalRef?.dismiss(null, 'cancel');
48572
- }
48573
- t(key) {
48574
- return this.i18n.t(key, this.i18nNamespace);
48575
- }
48576
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: EditOrgModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
48577
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: EditOrgModalComponent, isStandalone: true, selector: "val-edit-org-modal", inputs: { _modalRef: "_modalRef", org: "org", onSuccess: "onSuccess", i18nNamespace: "i18nNamespace" }, ngImport: i0, template: `
48578
- <ion-header>
48579
- <ion-toolbar>
48580
- <ion-buttons slot="end">
48581
- <ion-button fill="clear" color="dark" (click)="dismiss()">
48582
- <strong>{{ t('close') }}</strong>
48583
- </ion-button>
48584
- </ion-buttons>
48585
- </ion-toolbar>
48586
- </ion-header>
48587
- <ion-content class="ion-padding">
48588
- <val-display [props]="{ content: t('title'), size: 'small', color: 'dark' }" />
48589
- <val-title
48590
- [props]="{
48591
- content: t('subtitle'),
48592
- size: 'large',
48593
- color: '',
48594
- bold: false,
48595
- }"
48596
- />
48597
- <val-form [props]="formMeta()" (onSubmit)="onFormSubmit($event)" />
48598
- </ion-content>
48599
- `, isInline: true, dependencies: [{ 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: 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: IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: FormComponent, selector: "val-form", inputs: ["props"], outputs: ["onSubmit", "onInvalid", "onSelectChange"] }, { kind: "component", type: DisplayComponent, selector: "val-display", inputs: ["props"] }, { kind: "component", type: TitleComponent, selector: "val-title", inputs: ["props"] }] }); }
48600
- }
48601
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: EditOrgModalComponent, decorators: [{
48602
- type: Component,
48603
- args: [{
48604
- selector: 'val-edit-org-modal',
48605
- standalone: true,
48606
- imports: [IonHeader, IonToolbar, IonButtons, IonButton, IonContent, FormComponent, DisplayComponent, TitleComponent],
48607
- template: `
48608
- <ion-header>
48609
- <ion-toolbar>
48610
- <ion-buttons slot="end">
48611
- <ion-button fill="clear" color="dark" (click)="dismiss()">
48612
- <strong>{{ t('close') }}</strong>
48613
- </ion-button>
48614
- </ion-buttons>
48615
- </ion-toolbar>
48616
- </ion-header>
48617
- <ion-content class="ion-padding">
48618
- <val-display [props]="{ content: t('title'), size: 'small', color: 'dark' }" />
48619
- <val-title
48620
- [props]="{
48621
- content: t('subtitle'),
48622
- size: 'large',
48623
- color: '',
48624
- bold: false,
48625
- }"
48626
- />
48627
- <val-form [props]="formMeta()" (onSubmit)="onFormSubmit($event)" />
48628
- </ion-content>
48629
- `,
48630
- }]
48631
- }], ctorParameters: () => [], propDecorators: { _modalRef: [{
48632
- type: Input
48633
- }], org: [{
48634
- type: Input
48635
- }], onSuccess: [{
48636
- type: Input
48637
- }], i18nNamespace: [{
48638
- type: Input
48639
- }] } });
48640
-
48641
48641
  /**
48642
48642
  * Defaults i18n (es/en) embebidos en `val-transfer-ownership-modal`.
48643
48643
  * Auto-registrados en el constructor del componente si el consumer no proveyó el
@@ -48665,7 +48665,7 @@ const TRANSFER_OWNERSHIP_MODAL_I18N = {
48665
48665
  },
48666
48666
  };
48667
48667
 
48668
- const DEFAULT_NAMESPACE = 'Settings.TransferModal';
48668
+ const DEFAULT_NAMESPACE$1 = 'Settings.TransferModal';
48669
48669
  /**
48670
48670
  * `val-transfer-ownership-modal` — selector de miembro para transferir la
48671
48671
  * propiedad de la organización. Promovido desde `showcase` bajo el proceso de
@@ -48689,7 +48689,7 @@ class TransferOwnershipModalComponent {
48689
48689
  /** Lista de miembros transferibles (todos excepto el owner actual). */
48690
48690
  this.members = [];
48691
48691
  /** Namespace i18n con que la vista resuelve sus textos. */
48692
- this.i18nNamespace = DEFAULT_NAMESPACE;
48692
+ this.i18nNamespace = DEFAULT_NAMESPACE$1;
48693
48693
  this.query = signal('');
48694
48694
  this.selectedId = signal('');
48695
48695
  this.filtered = computed(() => {
@@ -48701,8 +48701,8 @@ class TransferOwnershipModalComponent {
48701
48701
  m.userId.toLowerCase().includes(q));
48702
48702
  });
48703
48703
  this.noResultsLabel = computed(() => `${this.t('noResults')} "${this.query()}"`);
48704
- if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE)) {
48705
- this.i18n.registerContent(DEFAULT_NAMESPACE, TRANSFER_OWNERSHIP_MODAL_I18N);
48704
+ if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$1)) {
48705
+ this.i18n.registerContent(DEFAULT_NAMESPACE$1, TRANSFER_OWNERSHIP_MODAL_I18N);
48706
48706
  }
48707
48707
  }
48708
48708
  ngOnInit() {
@@ -48887,6 +48887,1218 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
48887
48887
  type: Input
48888
48888
  }] } });
48889
48889
 
48890
+ /**
48891
+ * Defaults i18n (es/en) embebidos en `val-organization-view`. Auto-registrados en
48892
+ * el constructor del componente si el consumer no proveyó el namespace
48893
+ * (`Settings.Organization` por default). SOLO incluye las keys que la vista usa.
48894
+ *
48895
+ * Portado de `ORG_I18N_CONTENT` (showcase) bajo el proceso de ADR-021.
48896
+ */
48897
+ const ORGANIZATION_VIEW_I18N = {
48898
+ es: {
48899
+ pageTitle: 'Organización',
48900
+ pageDescription: 'Gestiona tu organización activa',
48901
+ infoTitle: 'Información',
48902
+ infoName: 'Nombre',
48903
+ infoDescription: 'Descripción',
48904
+ infoPlan: 'Plan',
48905
+ infoType: 'Tipo',
48906
+ editCta: 'Editar',
48907
+ saveCta: 'Guardar',
48908
+ cancelCta: 'Cancelar',
48909
+ membersTitle: 'Miembros',
48910
+ membersEmpty: 'Sin miembros aún.',
48911
+ memberRoles: 'Roles',
48912
+ inviteTitle: 'Invitar',
48913
+ inviteEmail: 'Correo electrónico',
48914
+ inviteRole: 'Rol',
48915
+ inviteCta: 'Invitar',
48916
+ inviteSuccess: 'Invitación enviada.',
48917
+ inviteError: 'No se pudo enviar la invitación.',
48918
+ membersError: 'No se pudieron cargar los miembros.',
48919
+ leaveTitle: 'Salir de la organización',
48920
+ leaveHint: 'Dejarás de tener acceso a esta organización.',
48921
+ leaveCta: 'Salir',
48922
+ leaveOwnerHint: 'Eres el propietario. Transfiere la propiedad antes de salir.',
48923
+ leaveConfirm: '¿Salir de la organización?',
48924
+ planFree: 'Gratuito',
48925
+ planPro: 'Pro',
48926
+ planEnterprise: 'Empresa',
48927
+ saveSuccess: 'Organización actualizada.',
48928
+ leaveSuccess: 'Has salido de la organización.',
48929
+ errorTitle: 'No se pudo cargar la organización',
48930
+ offlineTitle: 'Sin conexión',
48931
+ offlineHint: 'Verifica tu conexión e intenta de nuevo.',
48932
+ retry: 'Reintentar',
48933
+ saveError: 'No se pudo guardar. Intenta de nuevo.',
48934
+ leaveError: 'No se pudo salir. Intenta de nuevo.',
48935
+ transferTitle: 'Transferir propiedad',
48936
+ transferHint: 'Elige un miembro para transferirle la propiedad de la organización.',
48937
+ transferSelect: 'Seleccionar nuevo propietario',
48938
+ transferCta: 'Transferir',
48939
+ transferConfirm: '¿Transferir la propiedad a este miembro? Perderás el rol de propietario.',
48940
+ transferSuccess: 'Propiedad transferida.',
48941
+ transferError: 'No se pudo transferir la propiedad.',
48942
+ inviteCtaTitle: '¿Deseas agregar a alguien?',
48943
+ inviteCtaHint: 'Invita a usuarios a unirse a tu organización.',
48944
+ inviteOpen: 'Invitar',
48945
+ viewMemberCta: 'Ver miembro',
48946
+ roleViewer: 'Visor',
48947
+ roleEditor: 'Editor',
48948
+ roleAdmin: 'Admin',
48949
+ roleSuperAdmin: 'Super Admin',
48950
+ rolesSectionTitle: 'Roles y permisos',
48951
+ rolesHint: 'Tu organización tiene roles que controlan lo que cada miembro puede ver y hacer. Cada rol lleva un conjunto de permisos asociados.',
48952
+ rolesViewPerms: 'Ver roles y permisos',
48953
+ rolesSystemBadge: 'Sistema',
48954
+ permAll: 'Todo',
48955
+ permUsers: 'Usuarios',
48956
+ permDocuments: 'Documentos',
48957
+ permRoles: 'Roles',
48958
+ permMedia: 'Media',
48959
+ permTemplates: 'Plantillas',
48960
+ permRead: 'Leer',
48961
+ permWrite: 'Escribir',
48962
+ permCreate: 'Crear',
48963
+ permDelete: 'Eliminar',
48964
+ permManage: 'Gestionar',
48965
+ membersShowMore: 'Ver más',
48966
+ },
48967
+ en: {
48968
+ pageTitle: 'Organization',
48969
+ pageDescription: 'Manage your active organization',
48970
+ infoTitle: 'Information',
48971
+ infoName: 'Name',
48972
+ infoDescription: 'Description',
48973
+ infoPlan: 'Plan',
48974
+ infoType: 'Type',
48975
+ editCta: 'Edit',
48976
+ saveCta: 'Save',
48977
+ cancelCta: 'Cancel',
48978
+ membersTitle: 'Members',
48979
+ membersEmpty: 'No members yet.',
48980
+ memberRoles: 'Roles',
48981
+ inviteTitle: 'Invite',
48982
+ inviteEmail: 'Email address',
48983
+ inviteRole: 'Role',
48984
+ inviteCta: 'Invite',
48985
+ inviteSuccess: 'Invitation sent.',
48986
+ inviteError: 'Could not send invitation.',
48987
+ membersError: 'Could not load members.',
48988
+ leaveTitle: 'Leave organization',
48989
+ leaveHint: 'You will lose access to this organization.',
48990
+ leaveCta: 'Leave',
48991
+ leaveOwnerHint: 'You are the owner. Transfer ownership before leaving.',
48992
+ leaveConfirm: 'Leave this organization?',
48993
+ planFree: 'Free',
48994
+ planPro: 'Pro',
48995
+ planEnterprise: 'Enterprise',
48996
+ saveSuccess: 'Organization updated.',
48997
+ leaveSuccess: 'You have left the organization.',
48998
+ errorTitle: 'Could not load organization',
48999
+ offlineTitle: 'No connection',
49000
+ offlineHint: 'Check your connection and try again.',
49001
+ retry: 'Retry',
49002
+ saveError: 'Could not save. Please try again.',
49003
+ leaveError: 'Could not leave. Please try again.',
49004
+ transferTitle: 'Transfer ownership',
49005
+ transferHint: 'Choose a member to transfer ownership of this organization.',
49006
+ transferSelect: 'Select new owner',
49007
+ transferCta: 'Transfer',
49008
+ transferConfirm: 'Transfer ownership to this member? You will lose the owner role.',
49009
+ transferSuccess: 'Ownership transferred.',
49010
+ transferError: 'Could not transfer ownership.',
49011
+ inviteCtaTitle: 'Want to add someone?',
49012
+ inviteCtaHint: 'Invite users to join your organization.',
49013
+ inviteOpen: 'Invite',
49014
+ viewMemberCta: 'View member',
49015
+ roleViewer: 'Viewer',
49016
+ roleEditor: 'Editor',
49017
+ roleAdmin: 'Admin',
49018
+ roleSuperAdmin: 'Super Admin',
49019
+ rolesSectionTitle: 'Roles & permissions',
49020
+ rolesHint: 'Your organization has roles that control what each member can see and do. Each role carries a set of associated permissions.',
49021
+ rolesViewPerms: 'View roles & permissions',
49022
+ rolesSystemBadge: 'System',
49023
+ permAll: 'All',
49024
+ permUsers: 'Users',
49025
+ permDocuments: 'Documents',
49026
+ permRoles: 'Roles',
49027
+ permMedia: 'Media',
49028
+ permTemplates: 'Templates',
49029
+ permRead: 'Read',
49030
+ permWrite: 'Write',
49031
+ permCreate: 'Create',
49032
+ permDelete: 'Delete',
49033
+ permManage: 'Manage',
49034
+ membersShowMore: 'Show more',
49035
+ },
49036
+ };
49037
+
49038
+ const DEFAULT_NAMESPACE = 'Settings.Organization';
49039
+ const DEFAULT_VIEW_PERMISSIONS_ROUTE = '/app/settings/permissions';
49040
+ const DEFAULT_LEAVE_REDIRECT_ROUTE = '/app/settings/account';
49041
+ /**
49042
+ * `val-organization-view` — vista Gestión de organización full-feature
49043
+ * autocontenida (organism). Promovida desde `showcase` bajo el proceso de
49044
+ * ADR-021. Reúne:
49045
+ * - **Información** — nombre, descripción, plan y tipo de la org activa; editar
49046
+ * vía modal `val-edit-org-modal` (solo si `canEditOrg()`).
49047
+ * - **Miembros** — lista paginada (`val-member-card`) enriquecida con perfiles
49048
+ * públicos (Firestore); ver detalle vía `val-member-detail-modal`.
49049
+ * - **Roles y permisos** — sección explicativa con CTA a la ruta de permisos.
49050
+ * - **Invitar** — CTA que abre `val-invite-member-modal` (gateada por `users:*`).
49051
+ * - **Transferir propiedad** — `val-transfer-ownership-modal` (solo owner).
49052
+ * - **Salir de la organización** — `OrgService.leaveOrg` con confirmación
49053
+ * destructiva.
49054
+ *
49055
+ * **RBAC interno** (decisión CTO, ADR-021): el gating fino se resuelve dentro de
49056
+ * la vista vía `AuthService.hasPermission()` + `*valHasPermission` + computeds
49057
+ * `isOwner()`/`canManageMembers()`/`canEditOrg()`. NO se expone como config.
49058
+ *
49059
+ * **Auto-reload en org-switch**: un `effect` reactiona a `activeOrgId()` y recarga
49060
+ * org + miembros cuando el usuario cambia de organización activa mientras la vista
49061
+ * está montada. El effect cubre también la carga inicial (sin doble carga).
49062
+ *
49063
+ * NO renderiza ion-content — vive dentro de val-page-wrapper. `ActivatedRoute` se
49064
+ * inyecta `{ optional: true }` solo para leer el route data de config (no-op
49065
+ * embebido). Auto-registra sus defaults i18n (es/en) si el consumer no proveyó el
49066
+ * namespace (default `Settings.Organization`).
49067
+ */
49068
+ class OrganizationViewComponent {
49069
+ /** Namespace i18n resuelto (capturado para llamadas no-reactivas). */
49070
+ get ns() {
49071
+ return this.resolvedConfig().i18nNamespace;
49072
+ }
49073
+ constructor() {
49074
+ this.nav = inject(NavigationService);
49075
+ this.i18n = inject(I18nService);
49076
+ this.auth = inject(AuthService);
49077
+ this.orgService = inject(OrgService);
49078
+ this.toast = inject(ToastService);
49079
+ this.errors = inject(ValtechErrorService);
49080
+ this.modalService = inject(ModalService);
49081
+ this.firestore = inject(Firestore);
49082
+ this.confirmDialog = inject(ConfirmationDialogService);
49083
+ this.route = inject(ActivatedRoute, { optional: true });
49084
+ this.isDebug = isDevMode();
49085
+ this.resolvedConfig = computed(() => {
49086
+ const fromRoute = (this.route?.snapshot.data['organizationConfig'] ?? {});
49087
+ const merged = { ...fromRoute, ...(this.config ?? {}) };
49088
+ return {
49089
+ showInfo: merged.showInfo ?? true,
49090
+ showMembers: merged.showMembers ?? true,
49091
+ showRoles: merged.showRoles ?? true,
49092
+ showInvite: merged.showInvite ?? true,
49093
+ showTransferOwnership: merged.showTransferOwnership ?? true,
49094
+ showLeave: merged.showLeave ?? true,
49095
+ i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE,
49096
+ viewPermissionsRoute: merged.viewPermissionsRoute ?? DEFAULT_VIEW_PERMISSIONS_ROUTE,
49097
+ leaveRedirectRoute: merged.leaveRedirectRoute ?? DEFAULT_LEAVE_REDIRECT_ROUTE,
49098
+ onOrgEdited: merged.onOrgEdited,
49099
+ onMemberInvited: merged.onMemberInvited,
49100
+ onOwnershipTransferred: merged.onOwnershipTransferred,
49101
+ onLeftOrg: merged.onLeftOrg,
49102
+ };
49103
+ });
49104
+ this.org = signal(null);
49105
+ this.loading = signal(false);
49106
+ this.leaving = signal(false);
49107
+ this.orgLoadError = signal(null);
49108
+ this.orgErrorState = computed(() => {
49109
+ this.i18n.lang();
49110
+ const err = this.orgLoadError();
49111
+ if (!err)
49112
+ return null;
49113
+ return createErrorStateProps(err, {
49114
+ title: { offline: this.tt('offlineTitle'), error: this.tt('errorTitle') },
49115
+ description: {
49116
+ offline: this.tt('offlineHint'),
49117
+ error: interpretError(err).message,
49118
+ },
49119
+ retryLabel: this.tt('retry'),
49120
+ onRetry: () => this.loadOrg(),
49121
+ retrying: this.loading(),
49122
+ });
49123
+ });
49124
+ this.members = signal([]);
49125
+ this.membersLoading = signal(false);
49126
+ this.membersLoadError = signal(null);
49127
+ this.membersNextToken = signal(null);
49128
+ this.loadingMoreMembers = signal(false);
49129
+ this.showAllMembers = signal(false);
49130
+ this.visibleMembers = computed(() => (this.showAllMembers() ? this.members() : this.members().slice(0, 3)));
49131
+ this.transferring = signal(false);
49132
+ this.availableRoles = signal([
49133
+ { id: 'viewer', name: 'viewer' },
49134
+ { id: 'editor', name: 'editor' },
49135
+ { id: 'admin', name: 'admin' },
49136
+ ]);
49137
+ this.activeOrgId = computed(() => {
49138
+ const u = this.auth.user();
49139
+ return u?.activeOrgId ?? u?.activeOrg ?? '';
49140
+ });
49141
+ this.userId = computed(() => this.auth.user()?.userId ?? '');
49142
+ this.isOwner = computed(() => {
49143
+ const o = this.org();
49144
+ const uid = this.userId();
49145
+ return !!o && !!uid && o.ownerId === uid;
49146
+ });
49147
+ this.canEditOrg = computed(() => this.isOwner() || this.auth.hasPermission('org:manage'));
49148
+ this.canManageMembers = computed(() => {
49149
+ if (this.isOwner())
49150
+ return true;
49151
+ const uid = this.userId();
49152
+ const me = this.members().find(m => m.userId === uid);
49153
+ if (!me)
49154
+ return false;
49155
+ return me.roles.some(r => r === 'admin' || r === 'super_admin');
49156
+ });
49157
+ this.pageTitle = computed(() => this.tt('pageTitle'));
49158
+ this.pageDescription = computed(() => this.tt('pageDescription'));
49159
+ this.planLabel = computed(() => {
49160
+ const plan = this.org()?.plan;
49161
+ if (plan === 'pro')
49162
+ return this.tt('planPro');
49163
+ if (plan === 'enterprise')
49164
+ return this.tt('planEnterprise');
49165
+ return this.tt('planFree');
49166
+ });
49167
+ this.editButtonProps = computed(() => ({
49168
+ text: this.tt('editCta'),
49169
+ color: 'primary',
49170
+ fill: 'outline',
49171
+ size: 'small',
49172
+ shape: 'round',
49173
+ type: 'button',
49174
+ }));
49175
+ this.leaveButtonProps = computed(() => ({
49176
+ text: this.tt('leaveCta'),
49177
+ token: 'org-leave',
49178
+ color: 'dark',
49179
+ fill: 'outline',
49180
+ size: 'default',
49181
+ shape: 'round',
49182
+ type: 'button',
49183
+ state: this.isOwner() || this.leaving() ? ComponentStates.DISABLED : ComponentStates.ENABLED,
49184
+ }));
49185
+ this.transferableMembers = computed(() => this.members().filter(m => m.userId !== this.userId()));
49186
+ this.viewPermissionsButtonProps = computed(() => ({
49187
+ text: this.tt('rolesViewPerms'),
49188
+ color: 'primary',
49189
+ fill: 'outline',
49190
+ size: 'default',
49191
+ shape: 'round',
49192
+ type: 'button',
49193
+ token: 'org-view-permissions',
49194
+ }));
49195
+ this.transferButtonProps = computed(() => ({
49196
+ text: this.tt('transferCta'),
49197
+ token: 'org-transfer-submit',
49198
+ color: 'dark',
49199
+ fill: 'outline',
49200
+ size: 'default',
49201
+ shape: 'round',
49202
+ type: 'button',
49203
+ state: this.transferring() ? ComponentStates.DISABLED : ComponentStates.ENABLED,
49204
+ }));
49205
+ this.openInviteButtonProps = computed(() => ({
49206
+ text: this.tt('inviteOpen'),
49207
+ color: 'primary',
49208
+ fill: 'solid',
49209
+ size: 'default',
49210
+ shape: 'round',
49211
+ type: 'button',
49212
+ }));
49213
+ /** Guarda para detectar el primer disparo del effect (evita doble carga). */
49214
+ this.lastLoadedOrgId = null;
49215
+ const ns = this.ns;
49216
+ if (!this.i18n.hasNamespace(ns)) {
49217
+ this.i18n.registerContent(ns, ORGANIZATION_VIEW_I18N);
49218
+ }
49219
+ this.nav.setBackHeader('pageTitle', ns, { withMenu: true });
49220
+ // Auto-reload en org-switch: reacciona a activeOrgId() y recarga org + miembros
49221
+ // cuando el usuario cambia de organización activa mientras la vista está
49222
+ // montada. Cubre también la carga inicial — `lastLoadedOrgId` evita recargar
49223
+ // dos veces el mismo id (p.ej. si el effect re-corre por otra dependencia).
49224
+ // `allowSignalWrites` porque las load fns escriben signals síncronamente
49225
+ // (loading/error/token) — Angular 18 bloquea writes en effect sin el flag.
49226
+ effect(() => {
49227
+ const orgId = this.activeOrgId();
49228
+ if (orgId === this.lastLoadedOrgId)
49229
+ return;
49230
+ this.lastLoadedOrgId = orgId;
49231
+ this.loadOrg();
49232
+ this.loadMembers();
49233
+ this.loadRoles();
49234
+ }, { allowSignalWrites: true });
49235
+ connectPageRefresh(async () => {
49236
+ await Promise.all([this.loadOrg(), this.loadMembers()]);
49237
+ });
49238
+ }
49239
+ onEdit() {
49240
+ void this.modalService.open({
49241
+ component: EditOrgModalComponent,
49242
+ componentProps: {
49243
+ org: this.org(),
49244
+ onSuccess: (updated) => {
49245
+ this.org.set(updated);
49246
+ this.resolvedConfig().onOrgEdited?.();
49247
+ },
49248
+ },
49249
+ breakpoints: {
49250
+ breakpoints: [0, 0.85],
49251
+ initialBreakpoint: 0.85,
49252
+ showHandle: true,
49253
+ },
49254
+ backdropDismiss: true,
49255
+ });
49256
+ }
49257
+ async onLeave() {
49258
+ if (this.isOwner() || this.leaving())
49259
+ return;
49260
+ const result = await this.confirmDialog.confirmDestructive({
49261
+ title: this.tt('leaveTitle'),
49262
+ message: this.tt('leaveConfirm'),
49263
+ confirmButton: { text: this.tt('leaveCta') },
49264
+ cancelButton: { text: this.tt('cancelCta') },
49265
+ });
49266
+ if (!result.confirmed)
49267
+ return;
49268
+ const orgId = this.activeOrgId();
49269
+ if (!orgId)
49270
+ return;
49271
+ this.leaving.set(true);
49272
+ try {
49273
+ await firstValueFrom(this.orgService.leaveOrg(orgId));
49274
+ this.toast.show({
49275
+ message: this.tt('leaveSuccess'),
49276
+ color: 'dark',
49277
+ duration: 3500,
49278
+ });
49279
+ const cfg = this.resolvedConfig();
49280
+ this.nav.navigateByUrl(cfg.leaveRedirectRoute);
49281
+ cfg.onLeftOrg?.();
49282
+ }
49283
+ catch (err) {
49284
+ this.errors.handle(err, {
49285
+ context: 'organization.leave',
49286
+ fallbackKey: 'leaveError',
49287
+ i18nNamespace: this.ns,
49288
+ });
49289
+ }
49290
+ finally {
49291
+ this.leaving.set(false);
49292
+ }
49293
+ }
49294
+ onOpenInvite() {
49295
+ void this.modalService.open({
49296
+ component: InviteMemberModalComponent,
49297
+ componentProps: {
49298
+ orgId: this.activeOrgId(),
49299
+ existingMemberIds: this.members().map(m => m.userId),
49300
+ onSuccess: () => {
49301
+ this.loadMembers();
49302
+ this.resolvedConfig().onMemberInvited?.();
49303
+ },
49304
+ },
49305
+ breakpoints: {
49306
+ breakpoints: [0, 0.92],
49307
+ initialBreakpoint: 0.92,
49308
+ showHandle: true,
49309
+ },
49310
+ backdropDismiss: true,
49311
+ });
49312
+ }
49313
+ onViewPermissions() {
49314
+ this.nav.navigateByUrl(this.resolvedConfig().viewPermissionsRoute);
49315
+ }
49316
+ onOpenTransfer() {
49317
+ void this.modalService.open({
49318
+ component: TransferOwnershipModalComponent,
49319
+ componentProps: {
49320
+ members: this.transferableMembers(),
49321
+ onConfirm: (newOwnerId) => void this.onTransfer(newOwnerId),
49322
+ },
49323
+ breakpoints: {
49324
+ breakpoints: [0, 0.75],
49325
+ initialBreakpoint: 0.75,
49326
+ showHandle: true,
49327
+ },
49328
+ backdropDismiss: true,
49329
+ });
49330
+ }
49331
+ async onTransfer(newOwnerId) {
49332
+ if (!newOwnerId || this.transferring())
49333
+ return;
49334
+ const result = await this.confirmDialog.confirmDestructive({
49335
+ title: this.tt('transferTitle'),
49336
+ message: this.tt('transferConfirm'),
49337
+ confirmButton: { text: this.tt('transferCta') },
49338
+ cancelButton: { text: this.tt('cancelCta') },
49339
+ });
49340
+ if (!result.confirmed)
49341
+ return;
49342
+ const orgId = this.activeOrgId();
49343
+ if (!orgId)
49344
+ return;
49345
+ this.transferring.set(true);
49346
+ try {
49347
+ await firstValueFrom(this.orgService.transferOwnership(orgId, newOwnerId));
49348
+ this.toast.show({
49349
+ message: this.tt('transferSuccess'),
49350
+ color: 'dark',
49351
+ duration: 3500,
49352
+ });
49353
+ this.loadOrg();
49354
+ this.loadMembers();
49355
+ this.resolvedConfig().onOwnershipTransferred?.();
49356
+ }
49357
+ catch (err) {
49358
+ this.errors.handle(err, {
49359
+ context: 'organization.transfer',
49360
+ fallbackKey: 'transferError',
49361
+ i18nNamespace: this.ns,
49362
+ });
49363
+ }
49364
+ finally {
49365
+ this.transferring.set(false);
49366
+ }
49367
+ }
49368
+ loadOrg() {
49369
+ const orgId = this.activeOrgId();
49370
+ if (!orgId)
49371
+ return Promise.resolve();
49372
+ this.loading.set(true);
49373
+ this.orgLoadError.set(null);
49374
+ return new Promise(resolve => {
49375
+ this.orgService.getOrg(orgId).subscribe({
49376
+ next: org => {
49377
+ this.org.set(org);
49378
+ this.loading.set(false);
49379
+ resolve();
49380
+ },
49381
+ error: err => {
49382
+ this.orgLoadError.set(err);
49383
+ this.loading.set(false);
49384
+ resolve();
49385
+ },
49386
+ });
49387
+ });
49388
+ }
49389
+ loadMembers() {
49390
+ const orgId = this.activeOrgId();
49391
+ if (!orgId)
49392
+ return Promise.resolve();
49393
+ this.membersLoading.set(true);
49394
+ this.membersNextToken.set(null);
49395
+ this.showAllMembers.set(false);
49396
+ return new Promise(resolve => {
49397
+ this.orgService.getOrgMembers(orgId, { limit: 20 }).subscribe({
49398
+ next: async (res) => {
49399
+ const cards = await this.enrichWithProfiles(res.members);
49400
+ this.members.set(cards);
49401
+ this.membersNextToken.set(res.nextToken ?? null);
49402
+ this.membersLoading.set(false);
49403
+ resolve();
49404
+ },
49405
+ error: err => {
49406
+ this.membersLoadError.set(err);
49407
+ this.membersLoading.set(false);
49408
+ resolve();
49409
+ },
49410
+ });
49411
+ });
49412
+ }
49413
+ loadMoreMembers() {
49414
+ const orgId = this.activeOrgId();
49415
+ const token = this.membersNextToken();
49416
+ if (!orgId || !token || this.loadingMoreMembers())
49417
+ return;
49418
+ this.loadingMoreMembers.set(true);
49419
+ this.orgService.getOrgMembers(orgId, { limit: 20, nextToken: token }).subscribe({
49420
+ next: async (res) => {
49421
+ const cards = await this.enrichWithProfiles(res.members);
49422
+ this.members.update(prev => [...prev, ...cards]);
49423
+ this.membersNextToken.set(res.nextToken ?? null);
49424
+ this.loadingMoreMembers.set(false);
49425
+ },
49426
+ error: err => {
49427
+ this.errors.handle(err, {
49428
+ context: 'organization.loadMoreMembers',
49429
+ fallbackKey: 'membersError',
49430
+ i18nNamespace: this.ns,
49431
+ });
49432
+ this.loadingMoreMembers.set(false);
49433
+ },
49434
+ });
49435
+ }
49436
+ async enrichWithProfiles(members) {
49437
+ const results = await Promise.allSettled(members.map(async (m) => {
49438
+ try {
49439
+ const snap = await getDoc(doc(this.firestore, `profiles/${m.userId}`));
49440
+ const p = snap.exists() ? snap.data() : {};
49441
+ return {
49442
+ ...m,
49443
+ handle: p['handle'],
49444
+ avatar: (p['avatarUrl'] ?? p['avatar']),
49445
+ name: p['name'] || m.name,
49446
+ };
49447
+ }
49448
+ catch {
49449
+ return m;
49450
+ }
49451
+ }));
49452
+ return results.map(r => (r.status === 'fulfilled' ? r.value : {}));
49453
+ }
49454
+ loadRoles() {
49455
+ const orgId = this.activeOrgId();
49456
+ if (!orgId)
49457
+ return;
49458
+ this.orgService.listOrgRoles(orgId).subscribe({
49459
+ next: roles => {
49460
+ if (roles.length)
49461
+ this.availableRoles.set(roles);
49462
+ },
49463
+ });
49464
+ }
49465
+ roleLabel(roleIdOrName) {
49466
+ // Try to find by id first (UUID), then by name
49467
+ const found = this.availableRoles().find(r => r.id === roleIdOrName || r.name === roleIdOrName);
49468
+ const name = found?.name ?? roleIdOrName;
49469
+ const keyMap = {
49470
+ viewer: 'roleViewer',
49471
+ editor: 'roleEditor',
49472
+ admin: 'roleAdmin',
49473
+ super_admin: 'roleSuperAdmin',
49474
+ };
49475
+ const key = keyMap[name] ?? name;
49476
+ const label = this.tt(key);
49477
+ return label && !label.startsWith('[') ? label : name;
49478
+ }
49479
+ permissionLabel(perm) {
49480
+ if (perm === '*:*')
49481
+ return this.tt('permAll');
49482
+ const [resource, action] = perm.split(':');
49483
+ const resKey = {
49484
+ users: 'permUsers',
49485
+ documents: 'permDocuments',
49486
+ roles: 'permRoles',
49487
+ media: 'permMedia',
49488
+ templates: 'permTemplates',
49489
+ '*': 'permAll',
49490
+ };
49491
+ const actKey = {
49492
+ read: 'permRead',
49493
+ write: 'permWrite',
49494
+ create: 'permCreate',
49495
+ delete: 'permDelete',
49496
+ manage: 'permManage',
49497
+ '*': 'permAll',
49498
+ };
49499
+ const res = this.tt(resKey[resource] ?? resource);
49500
+ const act = this.tt(actKey[action] ?? action);
49501
+ return `${res}: ${act}`;
49502
+ }
49503
+ onViewMember(member) {
49504
+ void this.modalService.open({
49505
+ component: MemberDetailModalComponent,
49506
+ componentProps: {
49507
+ member,
49508
+ orgId: this.activeOrgId(),
49509
+ availableRoles: this.availableRoles(),
49510
+ canManage: this.canManageMembers() && member.userId !== this.userId(),
49511
+ isSelf: member.userId === this.userId(),
49512
+ onChanged: () => this.loadMembers(),
49513
+ },
49514
+ breakpoints: {
49515
+ breakpoints: [0, 0.7],
49516
+ initialBreakpoint: 0.7,
49517
+ showHandle: true,
49518
+ },
49519
+ backdropDismiss: true,
49520
+ });
49521
+ }
49522
+ tt(key) {
49523
+ return this.i18n.t(key, this.ns);
49524
+ }
49525
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: OrganizationViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
49526
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: OrganizationViewComponent, isStandalone: true, selector: "val-organization-view", inputs: { config: "config" }, ngImport: i0, template: `
49527
+ <div class="page">
49528
+ <header class="page-header">
49529
+ <val-display [props]="{ size: 'small', color: 'dark', content: pageTitle() }" />
49530
+ <val-title
49531
+ [props]="{
49532
+ size: 'large',
49533
+ color: 'dark',
49534
+ bold: false,
49535
+ content: pageDescription(),
49536
+ }"
49537
+ />
49538
+ </header>
49539
+
49540
+ @if (resolvedConfig().showInfo) {
49541
+ <!-- Org Info -->
49542
+ <section class="settings-section">
49543
+ <div class="section-header-row">
49544
+ <val-title
49545
+ [props]="{
49546
+ size: 'medium',
49547
+ color: 'dark',
49548
+ bold: true,
49549
+ content: tt('infoTitle'),
49550
+ }"
49551
+ />
49552
+ @if (canEditOrg()) {
49553
+ <val-button [props]="editButtonProps()" (click)="onEdit()" />
49554
+ }
49555
+ </div>
49556
+
49557
+ <div class="section-body">
49558
+ @if (loading()) {
49559
+ <val-skeleton-layout [props]="{ preset: 'detail', rows: 4 }" />
49560
+ } @else if (orgErrorState()) {
49561
+ <val-empty-state [props]="orgErrorState()!" />
49562
+ } @else {
49563
+ @if (org(); as o) {
49564
+ <div class="org-info-card">
49565
+ <div class="org-info-field org-info-field--full">
49566
+ <span class="org-info-label">{{ tt('infoName') }}</span>
49567
+ <span class="org-info-value">{{ o.name }}</span>
49568
+ </div>
49569
+ <div class="org-info-field org-info-field--full">
49570
+ <span class="org-info-label">{{ tt('infoDescription') }}</span>
49571
+ <span class="org-info-value org-info-value--muted">{{ o.description || '—' }}</span>
49572
+ </div>
49573
+ <div class="org-info-row">
49574
+ <div class="org-info-field">
49575
+ <span class="org-info-label">{{ tt('infoPlan') }}</span>
49576
+ <span class="plan-badge plan-badge--{{ o.plan }}">{{ planLabel() }}</span>
49577
+ </div>
49578
+ <div class="org-info-field">
49579
+ <span class="org-info-label">{{ tt('infoType') }}</span>
49580
+ <span class="org-info-value">{{ o.type }}</span>
49581
+ </div>
49582
+ </div>
49583
+ </div>
49584
+ }
49585
+ }
49586
+ </div>
49587
+ </section>
49588
+ }
49589
+
49590
+ @if (resolvedConfig().showMembers) {
49591
+ <!-- Members -->
49592
+ <section class="settings-section">
49593
+ <val-title
49594
+ [props]="{
49595
+ size: 'medium',
49596
+ color: 'dark',
49597
+ bold: true,
49598
+ content: tt('membersTitle'),
49599
+ }"
49600
+ />
49601
+ <div class="section-body">
49602
+ @if (membersLoading()) {
49603
+ <val-skeleton-layout [props]="{ preset: 'list', rows: 3 }" />
49604
+ } @else if (members().length === 0) {
49605
+ <val-text
49606
+ [props]="{
49607
+ content: tt('membersEmpty'),
49608
+ size: 'medium',
49609
+ color: 'dark',
49610
+ bold: false,
49611
+ }"
49612
+ />
49613
+ } @else {
49614
+ <div class="members-list">
49615
+ @for (m of visibleMembers(); track m.userId) {
49616
+ <val-member-card
49617
+ [attr.data-testid]="'org-member-' + m.userId"
49618
+ [props]="{
49619
+ id: m.userId,
49620
+ name: m.name,
49621
+ handle: m.handle,
49622
+ email: m.email,
49623
+ avatarUrl: m.avatar,
49624
+ roleLabel: m.roles[0] ? roleLabel(m.roles[0]) : '',
49625
+ actionAriaLabel: tt('viewMemberCta'),
49626
+ }"
49627
+ (onAction)="onViewMember(m)"
49628
+ />
49629
+ }
49630
+ </div>
49631
+ @if (!showAllMembers() && members().length > 3) {
49632
+ <button class="members-show-more" (click)="showAllMembers.set(true)">
49633
+ {{ tt('membersShowMore') }} ({{ members().length - 3 }})
49634
+ </button>
49635
+ }
49636
+ @if (showAllMembers() && membersNextToken()) {
49637
+ <button class="members-show-more" [disabled]="loadingMoreMembers()" (click)="loadMoreMembers()">
49638
+ {{ loadingMoreMembers() ? '…' : tt('membersShowMore') }}
49639
+ </button>
49640
+ }
49641
+ }
49642
+ </div>
49643
+ </section>
49644
+ }
49645
+
49646
+ @if (resolvedConfig().showRoles) {
49647
+ <!-- Roles y permisos: seccion explicativa, detalle en la ruta de permisos -->
49648
+ <section class="settings-section">
49649
+ <val-title
49650
+ [props]="{
49651
+ size: 'medium',
49652
+ color: 'dark',
49653
+ bold: true,
49654
+ content: tt('rolesSectionTitle'),
49655
+ }"
49656
+ />
49657
+ <div class="section-body">
49658
+ <val-text
49659
+ [props]="{
49660
+ content: tt('rolesHint'),
49661
+ size: 'medium',
49662
+ color: 'dark',
49663
+ bold: false,
49664
+ }"
49665
+ />
49666
+ <div class="row-actions">
49667
+ <val-button [props]="viewPermissionsButtonProps()" (click)="onViewPermissions()" />
49668
+ </div>
49669
+ </div>
49670
+ </section>
49671
+ }
49672
+
49673
+ @if (resolvedConfig().showInvite) {
49674
+ <!-- Invite: solo admin+ (gating RBAC interno) -->
49675
+ <section *valHasPermission="'users:*'" class="settings-section" data-testid="org-invite-section">
49676
+ <div class="invite-cta">
49677
+ <div class="invite-cta__text">
49678
+ <val-title
49679
+ [props]="{
49680
+ size: 'medium',
49681
+ color: 'dark',
49682
+ bold: true,
49683
+ content: tt('inviteCtaTitle'),
49684
+ }"
49685
+ />
49686
+ <val-text
49687
+ [props]="{
49688
+ size: 'medium',
49689
+ color: 'dark',
49690
+ bold: false,
49691
+ content: tt('inviteCtaHint'),
49692
+ }"
49693
+ />
49694
+ </div>
49695
+ <val-button [props]="openInviteButtonProps()" (click)="onOpenInvite()" />
49696
+ </div>
49697
+ </section>
49698
+ }
49699
+
49700
+ @if (resolvedConfig().showTransferOwnership && isOwner()) {
49701
+ <!-- Transfer ownership: solo owner (gating RBAC interno) -->
49702
+ <section class="settings-section" data-testid="org-transfer-section">
49703
+ <val-title
49704
+ [props]="{
49705
+ size: 'medium',
49706
+ color: 'dark',
49707
+ bold: true,
49708
+ content: tt('transferTitle'),
49709
+ }"
49710
+ />
49711
+ <div class="section-body">
49712
+ <val-text
49713
+ [props]="{
49714
+ content: tt('transferHint'),
49715
+ size: 'medium',
49716
+ color: 'dark',
49717
+ bold: false,
49718
+ }"
49719
+ />
49720
+ @if (transferableMembers().length > 0) {
49721
+ <div class="row-actions">
49722
+ <val-button [props]="transferButtonProps()" (click)="onOpenTransfer()" />
49723
+ </div>
49724
+ } @else {
49725
+ <val-text
49726
+ [props]="{
49727
+ content: tt('membersEmpty'),
49728
+ size: 'medium',
49729
+ color: 'dark',
49730
+ bold: false,
49731
+ }"
49732
+ />
49733
+ }
49734
+ </div>
49735
+ </section>
49736
+ }
49737
+
49738
+ @if (resolvedConfig().showLeave) {
49739
+ <!-- Leave org -->
49740
+ <section class="settings-section">
49741
+ <val-title
49742
+ [props]="{
49743
+ size: 'medium',
49744
+ color: 'dark',
49745
+ bold: true,
49746
+ content: tt('leaveTitle'),
49747
+ }"
49748
+ />
49749
+ <div class="section-body">
49750
+ <val-text
49751
+ [props]="{
49752
+ content: isOwner() ? tt('leaveOwnerHint') : tt('leaveHint'),
49753
+ size: 'medium',
49754
+ color: 'dark',
49755
+ bold: false,
49756
+ }"
49757
+ />
49758
+ <div class="row-actions">
49759
+ <val-button [props]="leaveButtonProps()" (click)="onLeave()" />
49760
+ </div>
49761
+ </div>
49762
+ </section>
49763
+ }
49764
+
49765
+ @if (isDebug) {
49766
+ <section class="settings-section rbac-debug">
49767
+ <div class="section-header-row">
49768
+ <val-title
49769
+ [props]="{
49770
+ content: 'RBAC (live)',
49771
+ size: 'small',
49772
+ color: 'medium',
49773
+ bold: false,
49774
+ }"
49775
+ />
49776
+ </div>
49777
+ <div class="rbac-debug__body">
49778
+ <div class="rbac-debug__row">
49779
+ <span class="rbac-debug__label">Roles</span>
49780
+ <span class="rbac-debug__value">{{ auth.roles().join(', ') || '—' }}</span>
49781
+ </div>
49782
+ <div class="rbac-debug__row">
49783
+ <span class="rbac-debug__label">Permisos</span>
49784
+ <span class="rbac-debug__value rbac-debug__value--perms">{{ auth.permissions().join(', ') || '—' }}</span>
49785
+ </div>
49786
+ </div>
49787
+ </section>
49788
+ }
49789
+ </div>
49790
+ `, isInline: true, styles: [".page{padding:16px 0;max-width:720px;margin:0 auto}.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;align-items:flex-start;justify-content:space-between;gap:16px;flex-wrap:wrap}.invite-cta__text{display:flex;flex-direction:column;gap:4px;flex:1}.org-info-card{border-radius:14px;background:var(--ion-color-light, #f4f5f8);overflow:hidden}: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--full{width:100%}.org-info-field:last-child{border-bottom:none}.org-info-row{display:flex}.org-info-row .org-info-field{flex:1}.org-info-row .org-info-field:first-child{border-right:1px solid var(--val-border-color, rgba(0, 0, 0, .06))}.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)}\n"], dependencies: [{ 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: 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"] }] }); }
49791
+ }
49792
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: OrganizationViewComponent, decorators: [{
49793
+ type: Component,
49794
+ args: [{ selector: 'val-organization-view', standalone: true, imports: [
49795
+ DisplayComponent,
49796
+ EmptyStateComponent,
49797
+ HasPermissionDirective,
49798
+ SkeletonLayoutComponent,
49799
+ TitleComponent,
49800
+ TextComponent,
49801
+ ButtonComponent,
49802
+ MemberCardComponent,
49803
+ TransferOwnershipModalComponent,
49804
+ ], template: `
49805
+ <div class="page">
49806
+ <header class="page-header">
49807
+ <val-display [props]="{ size: 'small', color: 'dark', content: pageTitle() }" />
49808
+ <val-title
49809
+ [props]="{
49810
+ size: 'large',
49811
+ color: 'dark',
49812
+ bold: false,
49813
+ content: pageDescription(),
49814
+ }"
49815
+ />
49816
+ </header>
49817
+
49818
+ @if (resolvedConfig().showInfo) {
49819
+ <!-- Org Info -->
49820
+ <section class="settings-section">
49821
+ <div class="section-header-row">
49822
+ <val-title
49823
+ [props]="{
49824
+ size: 'medium',
49825
+ color: 'dark',
49826
+ bold: true,
49827
+ content: tt('infoTitle'),
49828
+ }"
49829
+ />
49830
+ @if (canEditOrg()) {
49831
+ <val-button [props]="editButtonProps()" (click)="onEdit()" />
49832
+ }
49833
+ </div>
49834
+
49835
+ <div class="section-body">
49836
+ @if (loading()) {
49837
+ <val-skeleton-layout [props]="{ preset: 'detail', rows: 4 }" />
49838
+ } @else if (orgErrorState()) {
49839
+ <val-empty-state [props]="orgErrorState()!" />
49840
+ } @else {
49841
+ @if (org(); as o) {
49842
+ <div class="org-info-card">
49843
+ <div class="org-info-field org-info-field--full">
49844
+ <span class="org-info-label">{{ tt('infoName') }}</span>
49845
+ <span class="org-info-value">{{ o.name }}</span>
49846
+ </div>
49847
+ <div class="org-info-field org-info-field--full">
49848
+ <span class="org-info-label">{{ tt('infoDescription') }}</span>
49849
+ <span class="org-info-value org-info-value--muted">{{ o.description || '—' }}</span>
49850
+ </div>
49851
+ <div class="org-info-row">
49852
+ <div class="org-info-field">
49853
+ <span class="org-info-label">{{ tt('infoPlan') }}</span>
49854
+ <span class="plan-badge plan-badge--{{ o.plan }}">{{ planLabel() }}</span>
49855
+ </div>
49856
+ <div class="org-info-field">
49857
+ <span class="org-info-label">{{ tt('infoType') }}</span>
49858
+ <span class="org-info-value">{{ o.type }}</span>
49859
+ </div>
49860
+ </div>
49861
+ </div>
49862
+ }
49863
+ }
49864
+ </div>
49865
+ </section>
49866
+ }
49867
+
49868
+ @if (resolvedConfig().showMembers) {
49869
+ <!-- Members -->
49870
+ <section class="settings-section">
49871
+ <val-title
49872
+ [props]="{
49873
+ size: 'medium',
49874
+ color: 'dark',
49875
+ bold: true,
49876
+ content: tt('membersTitle'),
49877
+ }"
49878
+ />
49879
+ <div class="section-body">
49880
+ @if (membersLoading()) {
49881
+ <val-skeleton-layout [props]="{ preset: 'list', rows: 3 }" />
49882
+ } @else if (members().length === 0) {
49883
+ <val-text
49884
+ [props]="{
49885
+ content: tt('membersEmpty'),
49886
+ size: 'medium',
49887
+ color: 'dark',
49888
+ bold: false,
49889
+ }"
49890
+ />
49891
+ } @else {
49892
+ <div class="members-list">
49893
+ @for (m of visibleMembers(); track m.userId) {
49894
+ <val-member-card
49895
+ [attr.data-testid]="'org-member-' + m.userId"
49896
+ [props]="{
49897
+ id: m.userId,
49898
+ name: m.name,
49899
+ handle: m.handle,
49900
+ email: m.email,
49901
+ avatarUrl: m.avatar,
49902
+ roleLabel: m.roles[0] ? roleLabel(m.roles[0]) : '',
49903
+ actionAriaLabel: tt('viewMemberCta'),
49904
+ }"
49905
+ (onAction)="onViewMember(m)"
49906
+ />
49907
+ }
49908
+ </div>
49909
+ @if (!showAllMembers() && members().length > 3) {
49910
+ <button class="members-show-more" (click)="showAllMembers.set(true)">
49911
+ {{ tt('membersShowMore') }} ({{ members().length - 3 }})
49912
+ </button>
49913
+ }
49914
+ @if (showAllMembers() && membersNextToken()) {
49915
+ <button class="members-show-more" [disabled]="loadingMoreMembers()" (click)="loadMoreMembers()">
49916
+ {{ loadingMoreMembers() ? '…' : tt('membersShowMore') }}
49917
+ </button>
49918
+ }
49919
+ }
49920
+ </div>
49921
+ </section>
49922
+ }
49923
+
49924
+ @if (resolvedConfig().showRoles) {
49925
+ <!-- Roles y permisos: seccion explicativa, detalle en la ruta de permisos -->
49926
+ <section class="settings-section">
49927
+ <val-title
49928
+ [props]="{
49929
+ size: 'medium',
49930
+ color: 'dark',
49931
+ bold: true,
49932
+ content: tt('rolesSectionTitle'),
49933
+ }"
49934
+ />
49935
+ <div class="section-body">
49936
+ <val-text
49937
+ [props]="{
49938
+ content: tt('rolesHint'),
49939
+ size: 'medium',
49940
+ color: 'dark',
49941
+ bold: false,
49942
+ }"
49943
+ />
49944
+ <div class="row-actions">
49945
+ <val-button [props]="viewPermissionsButtonProps()" (click)="onViewPermissions()" />
49946
+ </div>
49947
+ </div>
49948
+ </section>
49949
+ }
49950
+
49951
+ @if (resolvedConfig().showInvite) {
49952
+ <!-- Invite: solo admin+ (gating RBAC interno) -->
49953
+ <section *valHasPermission="'users:*'" class="settings-section" data-testid="org-invite-section">
49954
+ <div class="invite-cta">
49955
+ <div class="invite-cta__text">
49956
+ <val-title
49957
+ [props]="{
49958
+ size: 'medium',
49959
+ color: 'dark',
49960
+ bold: true,
49961
+ content: tt('inviteCtaTitle'),
49962
+ }"
49963
+ />
49964
+ <val-text
49965
+ [props]="{
49966
+ size: 'medium',
49967
+ color: 'dark',
49968
+ bold: false,
49969
+ content: tt('inviteCtaHint'),
49970
+ }"
49971
+ />
49972
+ </div>
49973
+ <val-button [props]="openInviteButtonProps()" (click)="onOpenInvite()" />
49974
+ </div>
49975
+ </section>
49976
+ }
49977
+
49978
+ @if (resolvedConfig().showTransferOwnership && isOwner()) {
49979
+ <!-- Transfer ownership: solo owner (gating RBAC interno) -->
49980
+ <section class="settings-section" data-testid="org-transfer-section">
49981
+ <val-title
49982
+ [props]="{
49983
+ size: 'medium',
49984
+ color: 'dark',
49985
+ bold: true,
49986
+ content: tt('transferTitle'),
49987
+ }"
49988
+ />
49989
+ <div class="section-body">
49990
+ <val-text
49991
+ [props]="{
49992
+ content: tt('transferHint'),
49993
+ size: 'medium',
49994
+ color: 'dark',
49995
+ bold: false,
49996
+ }"
49997
+ />
49998
+ @if (transferableMembers().length > 0) {
49999
+ <div class="row-actions">
50000
+ <val-button [props]="transferButtonProps()" (click)="onOpenTransfer()" />
50001
+ </div>
50002
+ } @else {
50003
+ <val-text
50004
+ [props]="{
50005
+ content: tt('membersEmpty'),
50006
+ size: 'medium',
50007
+ color: 'dark',
50008
+ bold: false,
50009
+ }"
50010
+ />
50011
+ }
50012
+ </div>
50013
+ </section>
50014
+ }
50015
+
50016
+ @if (resolvedConfig().showLeave) {
50017
+ <!-- Leave org -->
50018
+ <section class="settings-section">
50019
+ <val-title
50020
+ [props]="{
50021
+ size: 'medium',
50022
+ color: 'dark',
50023
+ bold: true,
50024
+ content: tt('leaveTitle'),
50025
+ }"
50026
+ />
50027
+ <div class="section-body">
50028
+ <val-text
50029
+ [props]="{
50030
+ content: isOwner() ? tt('leaveOwnerHint') : tt('leaveHint'),
50031
+ size: 'medium',
50032
+ color: 'dark',
50033
+ bold: false,
50034
+ }"
50035
+ />
50036
+ <div class="row-actions">
50037
+ <val-button [props]="leaveButtonProps()" (click)="onLeave()" />
50038
+ </div>
50039
+ </div>
50040
+ </section>
50041
+ }
50042
+
50043
+ @if (isDebug) {
50044
+ <section class="settings-section rbac-debug">
50045
+ <div class="section-header-row">
50046
+ <val-title
50047
+ [props]="{
50048
+ content: 'RBAC (live)',
50049
+ size: 'small',
50050
+ color: 'medium',
50051
+ bold: false,
50052
+ }"
50053
+ />
50054
+ </div>
50055
+ <div class="rbac-debug__body">
50056
+ <div class="rbac-debug__row">
50057
+ <span class="rbac-debug__label">Roles</span>
50058
+ <span class="rbac-debug__value">{{ auth.roles().join(', ') || '—' }}</span>
50059
+ </div>
50060
+ <div class="rbac-debug__row">
50061
+ <span class="rbac-debug__label">Permisos</span>
50062
+ <span class="rbac-debug__value rbac-debug__value--perms">{{ auth.permissions().join(', ') || '—' }}</span>
50063
+ </div>
50064
+ </div>
50065
+ </section>
50066
+ }
50067
+ </div>
50068
+ `, styles: [".page{padding:16px 0;max-width:720px;margin:0 auto}.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;align-items:flex-start;justify-content:space-between;gap:16px;flex-wrap:wrap}.invite-cta__text{display:flex;flex-direction:column;gap:4px;flex:1}.org-info-card{border-radius:14px;background:var(--ion-color-light, #f4f5f8);overflow:hidden}: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--full{width:100%}.org-info-field:last-child{border-bottom:none}.org-info-row{display:flex}.org-info-row .org-info-field{flex:1}.org-info-row .org-info-field:first-child{border-right:1px solid var(--val-border-color, rgba(0, 0, 0, .06))}.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)}\n"] }]
50069
+ }], ctorParameters: () => [], propDecorators: { config: [{
50070
+ type: Input
50071
+ }] } });
50072
+
50073
+ /**
50074
+ * Helper para montar la vista Gestión de organización (`val-organization-view`)
50075
+ * como ruta en una app del factory. El `config` se pasa por route `data`
50076
+ * (`organizationConfig`) y el componente lo lee como fallback de su
50077
+ * `@Input() config`.
50078
+ *
50079
+ * @example
50080
+ * ```ts
50081
+ * // settings.routes.ts
50082
+ * export const settingsRoutes: Routes = [
50083
+ * ...provideValtechOrganizationRoutes(), // → /app/settings/organization
50084
+ * { path: 'preferences', loadComponent: () => ... },
50085
+ * ];
50086
+ *
50087
+ * // con config acotada:
50088
+ * ...provideValtechOrganizationRoutes({ config: { showLeave: false } }),
50089
+ * ...provideValtechOrganizationRoutes({ config: { viewPermissionsRoute: '/app/roles' } }),
50090
+ * ```
50091
+ */
50092
+ function provideValtechOrganizationRoutes(opts) {
50093
+ return [
50094
+ {
50095
+ path: opts?.path ?? 'organization',
50096
+ component: OrganizationViewComponent,
50097
+ data: { organizationConfig: opts?.config },
50098
+ },
50099
+ ];
50100
+ }
50101
+
48890
50102
  /** Built-in label sets for the three platform locales. */
48891
50103
  const CALLOUT_LABELS = {
48892
50104
  es: {
@@ -59211,5 +60423,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
59211
60423
  * Generated bundle index. Do not edit.
59212
60424
  */
59213
60425
 
59214
- export { ACTION_CARD_DEFAULTS, AD_SIZE_MAP, API_TABLE_COLUMN_LABELS, ARTICLE_CARD_DEFAULTS, ARTICLE_SPACING, ARTICLE_STRIP_DEFAULTS, AUTH_CTA_DEFAULTS, AVATAR_UPLOAD_DEFAULTS, AccordionComponent, AccountViewComponent, ActionCardComponent, ActionHeaderComponent, ActionType, AdSlotComponent, AdsLoaderService, AdsService, AlertBoxComponent, AnalyticsErrorHandler, AnalyticsRouterTracker, AnalyticsService, AnimatedTerminalComponent, AppConfigService, AppVersionService, ArticleBuilder, ArticleCardComponent, ArticleComponent, ArticleStripComponent, AttachmentUploaderComponent, AuthBackgroundComponent, AuthCtaComponent, AuthService, AuthStateService, AuthStorageService, AuthSyncService, AvatarComponent, AvatarUploadComponent, BOTTOM_NAV_DEFAULTS, BannerComponent, BaseDefault, BlogPostBuilder, BottomNavComponent, BoxComponent, BreadcrumbComponent, ButtonComponent, ButtonGroupComponent, CALLOUT_LABELS, CHEV_KEYS, CIRCLE_KEYS, COMMON_COUNTRY_CODES, COMMON_CURRENCIES, CONTENT_CARD_DEFAULTS, CORNER_KEYS, CTA_CARD_DEFAULTS, CURRENCY_INFO, CardComponent, CardSection, CardType, CardsCarouselComponent, ChangeEmailModalComponent, ChangePasswordModalComponent, CheckInputComponent, CheckboxRadioInputComponent, ChipGroupComponent, ClearDefault, ClearDefaultBlock, ClearDefaultFull, ClearDefaultRound, ClearDefaultRoundBlock, ClearDefaultRoundFull, CodeDisplayComponent, CommandDisplayComponent, CommentComponent, CommentInputComponent, CommentSectionComponent, CompanyFooterComponent, ComponentStates, ConfirmationDialogService, ContainerComponent, ContentCardComponent, ContentLoaderComponent, ContentReactionComponent, ContentService, ContentTransformer, CookieBannerComponent, CountdownComponent, CreateOrgModalComponent, CtaCardComponent, CurrencyInputComponent, DEFAULT_ADS_CONFIG, DEFAULT_APP_CONFIG_SERVICE_CONFIG, DEFAULT_APP_VERSION_SERVICE_CONFIG, DEFAULT_AUTH_CONFIG, DEFAULT_BACK_HEADER, DEFAULT_CANCEL_BUTTON, DEFAULT_CHECK_INTERVAL_MS, DEFAULT_CONFIRM_BUTTON, DEFAULT_COUNTDOWN_LABELS, DEFAULT_COUNTDOWN_LABELS_EN, DEFAULT_DEBUG_CONSOLE_CONFIG, DEFAULT_DONATION_CONFIG, 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, DEFAULT_SPLASH_SCREEN_CONFIG, DataTableComponent, DateInputComponent, DateRangeInputComponent, DebugConsoleComponent, DeleteAccountModalComponent, DetailSkeletonComponent, DeviceService, DisplayComponent, DividerComponent, DocsApiTableComponent, DocsBreadcrumbComponent, DocsBuilder, DocsCalloutComponent, DocsCodeExampleComponent, DocsLayoutComponent, DocsNavLinksComponent, DocsNavigationService, DocsPageComponent, DocsSearchComponent, DocsSectionComponent, DocsShellComponent, DocsSidebarComponent, DocsTocComponent, DonationService, DownloadService, EditOrgModalComponent, EmailInputComponent, EmptyStateComponent, ExpandableTextComponent, FEATURES_LIST_DEFAULTS, FUN_MODAL_DEFAULTS, FabComponent, FaqComponent, FeaturesListComponent, FeedbackFormComponent, FeedbackService, FileInputComponent, FirebaseService, FirestoreCollectionFactory, FirestoreService, FontSizeOption, FontSizeService, FooterComponent, FooterLinksComponent, FormComponent, FormSkeletonComponent, FunHeaderComponent, FunModalComponent, GlassComponent, GlowCardComponent, GlowComponent, GridSkeletonComponent, HANDOFF_ROUTE_PARAM, HANDOFF_TOKEN_PARAM, HandoffService, HasPermissionDirective, HeaderComponent, HintComponent, HorizontalScrollComponent, HourInputComponent, HrefComponent, I18nService, IMAGE_DEFAULTS, INFO_CARD_DEFAULTS, INITIAL_AUTH_STATE, INITIAL_MFA_STATE, INVITATION_CARD_DEFAULTS, Icon, IconComponent, IconService, ImageComponent, ImageCropComponent, ImageService, InAppBrowserService, InfiniteListComponent, InfoCardComponent, InfoComponent, InputI18nHelper, InputType, InvitationCardComponent, InviteMemberModalComponent, ItemListComponent, LANG_STORAGE_KEY$1 as LANG_STORAGE_KEY, LEGAL_CONTENT_CONFIG, LOGIN_DEFAULTS, LandingSplitComponent, LandingStepsComponent, LanguageSelectorComponent, LayeredCardComponent, LegalContentService, LegalLinkService, LinkComponent, LinkProcessorService, LinkedProvidersComponent, LinksAccordionComponent, LinksCakeComponent, ListSkeletonComponent, LoadingDirective, LocalStorageService, LocaleService, LoginAttemptModalComponent, LoginComponent, MEDIA_OBJECT_DEFAULTS, MEMBER_CARD_DEFAULTS, METRIC_CARD_DEFAULTS, MODAL_SIZES, MOTIF_KEYS, MOTION, MaintenancePageComponent, MarkdownArticleParserService, MediaObjectComponent, MemberCardComponent, MemberDetailModalComponent, MenuComponent, MessagingService, MetaService, MetricCardComponent, MfaModalComponent, ModalService, ModalShellComponent, MultiSelectSearchComponent, NUM_KEYS, NavigationService, NetworkBannerComponent, NetworkStatusService, NewsBuilder, NoContentComponent, NotesBoxComponent, NotificationActionService, NotificationsService, NumberFromToComponent, NumberInputComponent, NumberStepperComponent, OAUTH_PROVIDERS_INFO, OAuthCallbackComponent, OAuthService, OrgInfoSheetComponent, OrgService, OrgSwitchService, OutlineDefault, OutlineDefaultBlock, OutlineDefaultFull, OutlineDefaultRound, OutlineDefaultRoundBlock, OutlineDefaultRoundFull, PATTERN_MOTIFS, PATTERN_PALETTES, PATTERN_STYLE_CONFIGS, PLATFORM_CONFIGS, PageContentComponent, PageLinksComponent, PageRefreshService, PageTemplateComponent, PageWavesComponent, PageWrapperComponent, PaginationComponent, PaginationService, PasswordInputComponent, PatternComponent, PhoneInputComponent, PillComponent, PinInputComponent, PlainCodeBoxComponent, PopoverSelectorComponent, PreferencesService, PreferencesViewComponent, PresetService, PriceTagComponent, PrimarySolidBlockButton, PrimarySolidBlockHrefButton, PrimarySolidBlockIconButton, PrimarySolidBlockIconHrefButton, PrimarySolidDefaultRoundButton, PrimarySolidDefaultRoundHrefButton, PrimarySolidDefaultRoundIconButton, PrimarySolidDefaultRoundIconHrefButton, PrimarySolidFullButton, PrimarySolidFullHrefButton, PrimarySolidFullIconButton, PrimarySolidFullIconHrefButton, PrimarySolidLargeRoundButton, PrimarySolidLargeRoundHrefButton, PrimarySolidLargeRoundIconButton, PrimarySolidLargeRoundIconHrefButton, PrimarySolidSmallRoundButton, PrimarySolidSmallRoundHrefButton, PrimarySolidSmallRoundIconButton, PrimarySolidSmallRoundIconHrefButton, ProcessLinksPipe, ProfileSkeletonComponent, ProfileViewComponent, ProgressBarComponent, ProgressRingComponent, ProgressStatusComponent, PrompterComponent, QR_PRESETS, QrCodeComponent, QrGeneratorService, QueryBuilder, QuoteBoxComponent, REQUEST_STATUSES, RadioInputComponent, RangeInputComponent, RatingComponent, RefresherComponent, RequestFirestoreService, RequestFormBuilderService, RequestService, RightsFooterComponent, RotatingTextComponent, SEARCH_HEADER_DEFAULTS, SETTINGS_SECTIONS_CATALOG, SHAPE_KEYS, SHARE_PROFILE_MODAL_DEFAULTS, SKELETON_LAYOUT_DEFAULT_ROWS, SKELETON_PRESETS, SOLID_KEYS, STATS_BAR_DEFAULTS, STROKE_KEYS, SearchHeaderComponent, SearchSelectorComponent, SearchbarComponent, SecondarySolidBlockButton, SecondarySolidBlockHrefButton, SecondarySolidBlockIconButton, SecondarySolidBlockIconHrefButton, SecondarySolidDefaultRoundButton, SecondarySolidDefaultRoundHrefButton, SecondarySolidDefaultRoundIconButton, SecondarySolidDefaultRoundIconHrefButton, SecondarySolidFullButton, SecondarySolidFullHrefButton, SecondarySolidFullIconButton, SecondarySolidFullIconHrefButton, SecondarySolidLargeRoundButton, SecondarySolidLargeRoundHrefButton, SecondarySolidLargeRoundIconButton, SecondarySolidLargeRoundIconHrefButton, SecondarySolidSmallRoundButton, SecondarySolidSmallRoundHrefButton, SecondarySolidSmallRoundIconButton, SecondarySolidSmallRoundIconHrefButton, SecurityViewComponent, SegmentControlComponent, SelectSearchComponent, SessionListModalComponent, SessionService, SettingsHubComponent, ShareButtonsComponent, ShareProfileModalComponent, SimpleComponent, SkeletonComponent, SkeletonLayoutComponent, SkeletonService, SolidBlockButton, SolidDefault, SolidDefaultBlock, SolidDefaultButton, SolidDefaultFull, SolidDefaultRound, SolidDefaultRoundBlock, SolidDefaultRoundButton, SolidDefaultRoundFull, SolidFullButton, SolidLargeButton, SolidLargeRoundButton, SolidSmallButton, SolidSmallRoundButton, SplashScreenService, StatsBarComponent, StatsCardComponent, StepperComponent, StorageService, SwipeCarouselComponent, SwitchOrgModalComponent, TRI_KEYS, TabbedContentComponent, TableSkeletonComponent, TabsComponent, Terminal404Component, TestimonialCardComponent, TestimonialCarouselComponent, TextComponent, TextInputComponent, TextareaInputComponent, ThemeOption, ThemeService, TimelineComponent, TitleBlockComponent, TitleComponent, ToastService, ToggleInputComponent, TokenService, ToolbarActionType, ToolbarComponent, TransferOwnershipModalComponent, TranslatePipe, TypedCollection, UPDATE_BANNER_DEFAULT_CONTENT, UPDATE_BANNER_I18N_NAMESPACE, UpdateBannerComponent, UserAvatarComponent, UsernameInputComponent, VALTECH_ADS_CONFIG, VALTECH_APP_CONFIG, VALTECH_APP_VERSION, VALTECH_AUTH_CONFIG, VALTECH_COMPANY_LINKS, VALTECH_CONTENT_CONFIG, VALTECH_DEBUG_CONSOLE, VALTECH_DEFAULT_CONTENT, VALTECH_DONATION_CONFIG, VALTECH_FEEDBACK_CONFIG, VALTECH_FIREBASE_CONFIG, VALTECH_FOOTER_I18N, VALTECH_FOOTER_LOGO, VALTECH_LANGUAGE_SELECTOR, VALTECH_LEGAL_CONFIG, VALTECH_NETWORK_ERROR_KEY, VALTECH_SITE_PATHS, VALTECH_SOCIAL_LINKS, VALTECH_SPLASH_SCREEN, VALTECH_WEB_BASE_URLS, VERSION, ValtechErrorService, WizardComponent, WizardFooterComponent, applyDefaultValueToControl, authGuard, authInterceptor, authPasswordValidator, beautifyLegalArticle, blogPost, buildCompanyFooterProps, buildFooterLinks, buildLegalLinkResolver, buildPath, buildSettingsCards, collections, connectPageRefresh, createErrorStateProps, createFirebaseConfig, createGlowCardProps, createInitialPaginationState, createNumberFromToField, createRefreshableStream, createTitleProps, docs, errorLoggingInterceptor, extractPathParams, generatePatternTiles, generateRandomTile, getAppInfo, getAppVersion, getCollectionPath, getDocumentId, getTimeOfDayKey, goToTop, guestGuard, hasEmulators, interpretError, isAtEnd, isCollectionPath, isDocumentPath, isEmulatorMode, isValidPath, joinPath, maxLength, mulberry32, news, parseMarkdownArticle, permissionGuard, permissionGuardFromRoute, provideLegalContent, provideSplashScreen, provideValtechAccountRoutes, provideValtechAds, provideValtechAppConfig, provideValtechAppVersion, provideValtechAuth, provideValtechAuthInterceptor, provideValtechContent, provideValtechDebugConsole, provideValtechDonations, provideValtechErrorHandling, provideValtechFeedback, provideValtechFirebase, provideValtechI18n, provideValtechLegal, provideValtechPreferencesRoutes, provideValtechPresets, provideValtechProfileRoutes, provideValtechSecurityRoutes, provideValtechSettingsRoutes, provideValtechSite, provideValtechSkeleton, query, renderPatternSvgInner, replaceSpecialChars, resolveColor$1 as resolveColor, resolveInputDefaultValue, resolveWebBaseUrl, roleGuard, storagePaths, superAdminGuard, toArticle };
60426
+ export { ACTION_CARD_DEFAULTS, AD_SIZE_MAP, API_TABLE_COLUMN_LABELS, ARTICLE_CARD_DEFAULTS, ARTICLE_SPACING, ARTICLE_STRIP_DEFAULTS, AUTH_CTA_DEFAULTS, AVATAR_UPLOAD_DEFAULTS, AccordionComponent, AccountViewComponent, ActionCardComponent, ActionHeaderComponent, ActionType, AdSlotComponent, AdsLoaderService, AdsService, AlertBoxComponent, AnalyticsErrorHandler, AnalyticsRouterTracker, AnalyticsService, AnimatedTerminalComponent, AppConfigService, AppVersionService, ArticleBuilder, ArticleCardComponent, ArticleComponent, ArticleStripComponent, AttachmentUploaderComponent, AuthBackgroundComponent, AuthCtaComponent, AuthService, AuthStateService, AuthStorageService, AuthSyncService, AvatarComponent, AvatarUploadComponent, BOTTOM_NAV_DEFAULTS, BannerComponent, BaseDefault, BlogPostBuilder, BottomNavComponent, BoxComponent, BreadcrumbComponent, ButtonComponent, ButtonGroupComponent, CALLOUT_LABELS, CHEV_KEYS, CIRCLE_KEYS, COMMON_COUNTRY_CODES, COMMON_CURRENCIES, CONTENT_CARD_DEFAULTS, CORNER_KEYS, CTA_CARD_DEFAULTS, CURRENCY_INFO, CardComponent, CardSection, CardType, CardsCarouselComponent, ChangeEmailModalComponent, ChangePasswordModalComponent, CheckInputComponent, CheckboxRadioInputComponent, ChipGroupComponent, ClearDefault, ClearDefaultBlock, ClearDefaultFull, ClearDefaultRound, ClearDefaultRoundBlock, ClearDefaultRoundFull, CodeDisplayComponent, CommandDisplayComponent, CommentComponent, CommentInputComponent, CommentSectionComponent, CompanyFooterComponent, ComponentStates, ConfirmationDialogService, ContainerComponent, ContentCardComponent, ContentLoaderComponent, ContentReactionComponent, ContentService, ContentTransformer, CookieBannerComponent, CountdownComponent, CreateOrgModalComponent, CtaCardComponent, CurrencyInputComponent, DEFAULT_ADS_CONFIG, DEFAULT_APP_CONFIG_SERVICE_CONFIG, DEFAULT_APP_VERSION_SERVICE_CONFIG, DEFAULT_AUTH_CONFIG, DEFAULT_BACK_HEADER, DEFAULT_CANCEL_BUTTON, DEFAULT_CHECK_INTERVAL_MS, DEFAULT_CONFIRM_BUTTON, DEFAULT_COUNTDOWN_LABELS, DEFAULT_COUNTDOWN_LABELS_EN, DEFAULT_DEBUG_CONSOLE_CONFIG, DEFAULT_DONATION_CONFIG, 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, DEFAULT_SPLASH_SCREEN_CONFIG, DataTableComponent, DateInputComponent, DateRangeInputComponent, DebugConsoleComponent, DeleteAccountModalComponent, DetailSkeletonComponent, DeviceService, DisplayComponent, DividerComponent, DocsApiTableComponent, DocsBreadcrumbComponent, DocsBuilder, DocsCalloutComponent, DocsCodeExampleComponent, DocsLayoutComponent, DocsNavLinksComponent, DocsNavigationService, DocsPageComponent, DocsSearchComponent, DocsSectionComponent, DocsShellComponent, DocsSidebarComponent, DocsTocComponent, DonationService, DownloadService, EditOrgModalComponent, EmailInputComponent, EmptyStateComponent, ExpandableTextComponent, FEATURES_LIST_DEFAULTS, FUN_MODAL_DEFAULTS, FabComponent, FaqComponent, FeaturesListComponent, FeedbackFormComponent, FeedbackService, FileInputComponent, FirebaseService, FirestoreCollectionFactory, FirestoreService, FontSizeOption, FontSizeService, FooterComponent, FooterLinksComponent, FormComponent, FormSkeletonComponent, FunHeaderComponent, FunModalComponent, GlassComponent, GlowCardComponent, GlowComponent, GridSkeletonComponent, HANDOFF_ROUTE_PARAM, HANDOFF_TOKEN_PARAM, HandoffService, HasPermissionDirective, HeaderComponent, HintComponent, HorizontalScrollComponent, HourInputComponent, HrefComponent, I18nService, IMAGE_DEFAULTS, INFO_CARD_DEFAULTS, INITIAL_AUTH_STATE, INITIAL_MFA_STATE, INVITATION_CARD_DEFAULTS, Icon, IconComponent, IconService, ImageComponent, ImageCropComponent, ImageService, InAppBrowserService, InfiniteListComponent, InfoCardComponent, InfoComponent, InputI18nHelper, InputType, InvitationCardComponent, InviteMemberModalComponent, ItemListComponent, LANG_STORAGE_KEY$1 as LANG_STORAGE_KEY, LEGAL_CONTENT_CONFIG, LOGIN_DEFAULTS, LandingSplitComponent, LandingStepsComponent, LanguageSelectorComponent, LayeredCardComponent, LegalContentService, LegalLinkService, LinkComponent, LinkProcessorService, LinkedProvidersComponent, LinksAccordionComponent, LinksCakeComponent, ListSkeletonComponent, LoadingDirective, LocalStorageService, LocaleService, LoginAttemptModalComponent, LoginComponent, MEDIA_OBJECT_DEFAULTS, MEMBER_CARD_DEFAULTS, METRIC_CARD_DEFAULTS, MODAL_SIZES, MOTIF_KEYS, MOTION, MaintenancePageComponent, MarkdownArticleParserService, MediaObjectComponent, MemberCardComponent, MemberDetailModalComponent, MenuComponent, MessagingService, MetaService, MetricCardComponent, MfaModalComponent, ModalService, ModalShellComponent, MultiSelectSearchComponent, NUM_KEYS, NavigationService, NetworkBannerComponent, NetworkStatusService, NewsBuilder, NoContentComponent, NotesBoxComponent, NotificationActionService, NotificationsService, NumberFromToComponent, NumberInputComponent, NumberStepperComponent, OAUTH_PROVIDERS_INFO, OAuthCallbackComponent, OAuthService, OrgInfoSheetComponent, OrgService, OrgSwitchService, OrganizationViewComponent, OutlineDefault, OutlineDefaultBlock, OutlineDefaultFull, OutlineDefaultRound, OutlineDefaultRoundBlock, OutlineDefaultRoundFull, PATTERN_MOTIFS, PATTERN_PALETTES, PATTERN_STYLE_CONFIGS, PLATFORM_CONFIGS, PageContentComponent, PageLinksComponent, PageRefreshService, PageTemplateComponent, PageWavesComponent, PageWrapperComponent, PaginationComponent, PaginationService, PasswordInputComponent, PatternComponent, PhoneInputComponent, PillComponent, PinInputComponent, PlainCodeBoxComponent, PopoverSelectorComponent, PreferencesService, PreferencesViewComponent, PresetService, PriceTagComponent, PrimarySolidBlockButton, PrimarySolidBlockHrefButton, PrimarySolidBlockIconButton, PrimarySolidBlockIconHrefButton, PrimarySolidDefaultRoundButton, PrimarySolidDefaultRoundHrefButton, PrimarySolidDefaultRoundIconButton, PrimarySolidDefaultRoundIconHrefButton, PrimarySolidFullButton, PrimarySolidFullHrefButton, PrimarySolidFullIconButton, PrimarySolidFullIconHrefButton, PrimarySolidLargeRoundButton, PrimarySolidLargeRoundHrefButton, PrimarySolidLargeRoundIconButton, PrimarySolidLargeRoundIconHrefButton, PrimarySolidSmallRoundButton, PrimarySolidSmallRoundHrefButton, PrimarySolidSmallRoundIconButton, PrimarySolidSmallRoundIconHrefButton, ProcessLinksPipe, ProfileSkeletonComponent, ProfileViewComponent, ProgressBarComponent, ProgressRingComponent, ProgressStatusComponent, PrompterComponent, QR_PRESETS, QrCodeComponent, QrGeneratorService, QueryBuilder, QuoteBoxComponent, REQUEST_STATUSES, RadioInputComponent, RangeInputComponent, RatingComponent, RefresherComponent, RequestFirestoreService, RequestFormBuilderService, RequestService, RightsFooterComponent, RotatingTextComponent, SEARCH_HEADER_DEFAULTS, SETTINGS_SECTIONS_CATALOG, SHAPE_KEYS, SHARE_PROFILE_MODAL_DEFAULTS, SKELETON_LAYOUT_DEFAULT_ROWS, SKELETON_PRESETS, SOLID_KEYS, STATS_BAR_DEFAULTS, STROKE_KEYS, SearchHeaderComponent, SearchSelectorComponent, SearchbarComponent, SecondarySolidBlockButton, SecondarySolidBlockHrefButton, SecondarySolidBlockIconButton, SecondarySolidBlockIconHrefButton, SecondarySolidDefaultRoundButton, SecondarySolidDefaultRoundHrefButton, SecondarySolidDefaultRoundIconButton, SecondarySolidDefaultRoundIconHrefButton, SecondarySolidFullButton, SecondarySolidFullHrefButton, SecondarySolidFullIconButton, SecondarySolidFullIconHrefButton, SecondarySolidLargeRoundButton, SecondarySolidLargeRoundHrefButton, SecondarySolidLargeRoundIconButton, SecondarySolidLargeRoundIconHrefButton, SecondarySolidSmallRoundButton, SecondarySolidSmallRoundHrefButton, SecondarySolidSmallRoundIconButton, SecondarySolidSmallRoundIconHrefButton, SecurityViewComponent, SegmentControlComponent, SelectSearchComponent, SessionListModalComponent, SessionService, SettingsHubComponent, ShareButtonsComponent, ShareProfileModalComponent, SimpleComponent, SkeletonComponent, SkeletonLayoutComponent, SkeletonService, SolidBlockButton, SolidDefault, SolidDefaultBlock, SolidDefaultButton, SolidDefaultFull, SolidDefaultRound, SolidDefaultRoundBlock, SolidDefaultRoundButton, SolidDefaultRoundFull, SolidFullButton, SolidLargeButton, SolidLargeRoundButton, SolidSmallButton, SolidSmallRoundButton, SplashScreenService, StatsBarComponent, StatsCardComponent, StepperComponent, StorageService, SwipeCarouselComponent, SwitchOrgModalComponent, TRI_KEYS, TabbedContentComponent, TableSkeletonComponent, TabsComponent, Terminal404Component, TestimonialCardComponent, TestimonialCarouselComponent, TextComponent, TextInputComponent, TextareaInputComponent, ThemeOption, ThemeService, TimelineComponent, TitleBlockComponent, TitleComponent, ToastService, ToggleInputComponent, TokenService, ToolbarActionType, ToolbarComponent, TransferOwnershipModalComponent, TranslatePipe, TypedCollection, UPDATE_BANNER_DEFAULT_CONTENT, UPDATE_BANNER_I18N_NAMESPACE, UpdateBannerComponent, UserAvatarComponent, UsernameInputComponent, VALTECH_ADS_CONFIG, VALTECH_APP_CONFIG, VALTECH_APP_VERSION, VALTECH_AUTH_CONFIG, VALTECH_COMPANY_LINKS, VALTECH_CONTENT_CONFIG, VALTECH_DEBUG_CONSOLE, VALTECH_DEFAULT_CONTENT, VALTECH_DONATION_CONFIG, VALTECH_FEEDBACK_CONFIG, VALTECH_FIREBASE_CONFIG, VALTECH_FOOTER_I18N, VALTECH_FOOTER_LOGO, VALTECH_LANGUAGE_SELECTOR, VALTECH_LEGAL_CONFIG, VALTECH_NETWORK_ERROR_KEY, VALTECH_SITE_PATHS, VALTECH_SOCIAL_LINKS, VALTECH_SPLASH_SCREEN, VALTECH_WEB_BASE_URLS, VERSION, ValtechErrorService, WizardComponent, WizardFooterComponent, applyDefaultValueToControl, authGuard, authInterceptor, authPasswordValidator, beautifyLegalArticle, blogPost, buildCompanyFooterProps, buildFooterLinks, buildLegalLinkResolver, buildPath, buildSettingsCards, collections, connectPageRefresh, createErrorStateProps, createFirebaseConfig, createGlowCardProps, createInitialPaginationState, createNumberFromToField, createRefreshableStream, createTitleProps, docs, errorLoggingInterceptor, extractPathParams, generatePatternTiles, generateRandomTile, getAppInfo, getAppVersion, getCollectionPath, getDocumentId, getTimeOfDayKey, goToTop, guestGuard, hasEmulators, interpretError, isAtEnd, isCollectionPath, isDocumentPath, isEmulatorMode, isValidPath, joinPath, maxLength, mulberry32, news, parseMarkdownArticle, permissionGuard, permissionGuardFromRoute, provideLegalContent, provideSplashScreen, provideValtechAccountRoutes, provideValtechAds, provideValtechAppConfig, provideValtechAppVersion, provideValtechAuth, provideValtechAuthInterceptor, provideValtechContent, provideValtechDebugConsole, provideValtechDonations, provideValtechErrorHandling, provideValtechFeedback, provideValtechFirebase, provideValtechI18n, provideValtechLegal, provideValtechOrganizationRoutes, provideValtechPreferencesRoutes, provideValtechPresets, provideValtechProfileRoutes, provideValtechSecurityRoutes, provideValtechSettingsRoutes, provideValtechSite, provideValtechSkeleton, query, renderPatternSvgInner, replaceSpecialChars, resolveColor$1 as resolveColor, resolveInputDefaultValue, resolveWebBaseUrl, roleGuard, storagePaths, superAdminGuard, toArticle };
59215
60427
  //# sourceMappingURL=valtech-components.mjs.map