valtech-components 2.0.997 → 2.0.998

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.
Files changed (30) hide show
  1. package/esm2022/lib/components/organisms/api-keys-modal/api-keys-modal.component.mjs +385 -0
  2. package/esm2022/lib/components/organisms/api-keys-modal/api-keys-modal.i18n.mjs +63 -0
  3. package/esm2022/lib/components/organisms/member-import-modal/member-import-modal.component.mjs +313 -0
  4. package/esm2022/lib/components/organisms/member-import-modal/member-import-modal.i18n.mjs +63 -0
  5. package/esm2022/lib/components/organisms/organization-view/organization-view.component.mjs +76 -1
  6. package/esm2022/lib/components/organisms/organization-view/organization-view.i18n.mjs +5 -1
  7. package/esm2022/lib/services/apikeys/api-keys.service.mjs +46 -0
  8. package/esm2022/lib/services/apikeys/types.mjs +7 -0
  9. package/esm2022/lib/services/auth/auth.service.mjs +14 -1
  10. package/esm2022/lib/services/auth/types.mjs +1 -1
  11. package/esm2022/lib/services/org/org.service.mjs +8 -1
  12. package/esm2022/lib/services/org/types.mjs +1 -1
  13. package/esm2022/lib/version.mjs +2 -2
  14. package/esm2022/public-api.mjs +4 -1
  15. package/fesm2022/valtech-components.mjs +982 -42
  16. package/fesm2022/valtech-components.mjs.map +1 -1
  17. package/lib/components/organisms/api-keys-modal/api-keys-modal.component.d.ts +44 -0
  18. package/lib/components/organisms/api-keys-modal/api-keys-modal.i18n.d.ts +6 -0
  19. package/lib/components/organisms/member-import-modal/member-import-modal.component.d.ts +47 -0
  20. package/lib/components/organisms/member-import-modal/member-import-modal.i18n.d.ts +6 -0
  21. package/lib/components/organisms/organization-view/organization-view.component.d.ts +5 -0
  22. package/lib/services/apikeys/api-keys.service.d.ts +25 -0
  23. package/lib/services/apikeys/types.d.ts +46 -0
  24. package/lib/services/auth/auth.service.d.ts +7 -1
  25. package/lib/services/auth/types.d.ts +23 -0
  26. package/lib/services/org/org.service.d.ts +6 -1
  27. package/lib/services/org/types.d.ts +30 -0
  28. package/lib/version.d.ts +1 -1
  29. package/package.json +1 -1
  30. package/public-api.d.ts +5 -1
@@ -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.997';
57
+ const VERSION = '2.0.998';
58
58
 
59
59
  // Control de estado de refresco (singleton a nivel de módulo)
60
60
  let isRefreshing = false;
@@ -8658,6 +8658,19 @@ class AuthService {
8658
8658
  .post(`${this.baseUrl}/reset-password`, request)
8659
8659
  .pipe(catchError(error => this.handleAuthError(error)));
8660
8660
  }
8661
+ /**
8662
+ * Activa una cuenta pre-aprovisionada por una organización (ADR-023): define
8663
+ * la contraseña con el token del enlace de email. En éxito el backend devuelve
8664
+ * tokens de auto-signin → el usuario queda logueado (mismo flujo que verify-email).
8665
+ */
8666
+ activateAccount(request) {
8667
+ this.stateService.clearError();
8668
+ return this.http.post(`${this.baseUrl}/activate`, request).pipe(tap(response => {
8669
+ if (response.activated && response.accessToken) {
8670
+ this.handleSuccessfulAuth(response);
8671
+ }
8672
+ }), catchError(error => this.handleAuthError(error)));
8673
+ }
8661
8674
  /**
8662
8675
  * Cambia la contraseña del usuario autenticado.
8663
8676
  * Requiere la contraseña actual para verificación.
@@ -41737,7 +41750,7 @@ const PROFILE_VIEW_I18N = {
41737
41750
  const HANDLE_PATTERN = /^[a-z_][a-z0-9_]*$/;
41738
41751
  const HANDLE_MIN = 3;
41739
41752
  const HANDLE_MAX = 20;
41740
- const DEFAULT_NAMESPACE$f = 'Settings.Profile';
41753
+ const DEFAULT_NAMESPACE$h = 'Settings.Profile';
41741
41754
  /**
41742
41755
  * `val-profile-view` — vista Perfil full-feature autocontenida (organism).
41743
41756
  *
@@ -41787,7 +41800,7 @@ class ProfileViewComponent {
41787
41800
  showPhone: merged.showPhone ?? true,
41788
41801
  showShareCta: merged.showShareCta ?? true,
41789
41802
  profileBaseUrl: merged.profileBaseUrl ?? '',
41790
- i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$f,
41803
+ i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$h,
41791
41804
  onSaved: merged.onSaved,
41792
41805
  onAvatarUploaded: merged.onAvatarUploaded,
41793
41806
  };
@@ -42703,7 +42716,7 @@ const PREFERENCES_VIEW_I18N = {
42703
42716
  },
42704
42717
  };
42705
42718
 
42706
- const DEFAULT_NAMESPACE$e = 'Settings.General';
42719
+ const DEFAULT_NAMESPACE$g = 'Settings.General';
42707
42720
  const DEFAULT_LANGUAGES = ['es', 'en'];
42708
42721
  /**
42709
42722
  * `val-preferences-view` — vista de preferencias de UI cross-app (tema +
@@ -42740,7 +42753,7 @@ class PreferencesViewComponent {
42740
42753
  showLanguage: merged.showLanguage ?? true,
42741
42754
  showFontSize: merged.showFontSize ?? true,
42742
42755
  supportedLanguages: merged.supportedLanguages ?? DEFAULT_LANGUAGES,
42743
- i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$e,
42756
+ i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$g,
42744
42757
  onThemeChanged: merged.onThemeChanged,
42745
42758
  onLanguageChanged: merged.onLanguageChanged,
42746
42759
  onFontSizeChanged: merged.onFontSizeChanged,
@@ -44496,7 +44509,7 @@ const SECURITY_VIEW_I18N = {
44496
44509
  };
44497
44510
 
44498
44511
  addIcons({ laptopOutline, phonePortraitOutline });
44499
- const DEFAULT_NAMESPACE$d = 'Settings.Security';
44512
+ const DEFAULT_NAMESPACE$f = 'Settings.Security';
44500
44513
  /**
44501
44514
  * `val-security-view` — vista Seguridad full-feature autocontenida (organism).
44502
44515
  *
@@ -44545,7 +44558,7 @@ class SecurityViewComponent {
44545
44558
  showChangeEmail: merged.showChangeEmail ?? true,
44546
44559
  showMfa: merged.showMfa ?? true,
44547
44560
  showPolicies: merged.showPolicies ?? true,
44548
- i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$d,
44561
+ i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$f,
44549
44562
  handleDeepLinks: merged.handleDeepLinks ?? true,
44550
44563
  homeRoute: merged.homeRoute ?? '/app/home',
44551
44564
  onMfaCompleted: merged.onMfaCompleted,
@@ -45347,6 +45360,13 @@ class OrgService {
45347
45360
  inviteUser(orgId, req) {
45348
45361
  return this.http.post(`${this.baseUrl}/${orgId}/invite`, req);
45349
45362
  }
45363
+ /**
45364
+ * Crea/asigna miembros en masa (ADR-023). Resultado por-fila (partial success):
45365
+ * `created` (pre-aprovisionado), `assigned` (ya existía), `skipped`, `error`.
45366
+ */
45367
+ importMembers(orgId, req) {
45368
+ return this.http.post(`${this.baseUrl}/${orgId}/members/import`, req);
45369
+ }
45350
45370
  leaveOrg(orgId) {
45351
45371
  return this.http.post(`${this.baseUrl}/${orgId}/leave`, {});
45352
45372
  }
@@ -45451,7 +45471,7 @@ const CREATE_ORG_MODAL_I18N = {
45451
45471
  },
45452
45472
  };
45453
45473
 
45454
- const DEFAULT_NAMESPACE$c = 'CreateOrgModal';
45474
+ const DEFAULT_NAMESPACE$e = 'CreateOrgModal';
45455
45475
  /**
45456
45476
  * `val-create-org-modal` — modal de creación de organización (organism
45457
45477
  * compartido). Promovido desde `showcase` bajo el proceso de ADR-021. Reusado por
@@ -45477,10 +45497,10 @@ class CreateOrgModalComponent {
45477
45497
  this.toast = inject(ToastService);
45478
45498
  this.errors = inject(ValtechErrorService);
45479
45499
  /** Namespace i18n con que la vista resuelve sus textos. */
45480
- this.i18nNamespace = DEFAULT_NAMESPACE$c;
45500
+ this.i18nNamespace = DEFAULT_NAMESPACE$e;
45481
45501
  this._busy = signal(false);
45482
- if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$c)) {
45483
- this.i18n.registerContent(DEFAULT_NAMESPACE$c, CREATE_ORG_MODAL_I18N);
45502
+ if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$e)) {
45503
+ this.i18n.registerContent(DEFAULT_NAMESPACE$e, CREATE_ORG_MODAL_I18N);
45484
45504
  }
45485
45505
  this.formMeta = signal(this.buildFormMeta());
45486
45506
  effect(() => {
@@ -45716,7 +45736,7 @@ const DELETE_ACCOUNT_MODAL_I18N = {
45716
45736
  },
45717
45737
  };
45718
45738
 
45719
- const DEFAULT_NAMESPACE$b = 'Settings.DeleteAccount';
45739
+ const DEFAULT_NAMESPACE$d = 'Settings.DeleteAccount';
45720
45740
  /**
45721
45741
  * `val-delete-account-modal` — modal de eliminación de cuenta (organism,
45722
45742
  * cuenta-específico). Promovido desde `showcase` bajo el proceso de ADR-021.
@@ -45742,7 +45762,7 @@ class DeleteAccountModalComponent {
45742
45762
  this.i18n = inject(I18nService);
45743
45763
  this.toast = inject(ToastService);
45744
45764
  /** Namespace i18n con que la vista resuelve sus textos. */
45745
- this.i18nNamespace = DEFAULT_NAMESPACE$b;
45765
+ this.i18nNamespace = DEFAULT_NAMESPACE$d;
45746
45766
  this.hasPassword = signal(null);
45747
45767
  this.codeSent = signal(false);
45748
45768
  this.formState = signal(ComponentStates.ENABLED);
@@ -45809,8 +45829,8 @@ class DeleteAccountModalComponent {
45809
45829
  ],
45810
45830
  actions: this.submitBtn('delete-code-submit', 'confirmCta'),
45811
45831
  }));
45812
- if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$b)) {
45813
- this.i18n.registerContent(DEFAULT_NAMESPACE$b, DELETE_ACCOUNT_MODAL_I18N);
45832
+ if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$d)) {
45833
+ this.i18n.registerContent(DEFAULT_NAMESPACE$d, DELETE_ACCOUNT_MODAL_I18N);
45814
45834
  }
45815
45835
  }
45816
45836
  ngOnInit() {
@@ -46011,7 +46031,7 @@ const ORG_INFO_SHEET_I18N = {
46011
46031
  },
46012
46032
  };
46013
46033
 
46014
- const DEFAULT_NAMESPACE$a = 'OrgInfoSheet';
46034
+ const DEFAULT_NAMESPACE$c = 'OrgInfoSheet';
46015
46035
  /**
46016
46036
  * `val-org-info-sheet` — sheet informativo "¿Qué son las organizaciones?".
46017
46037
  * Extraído del `OrgInfoSheetComponent` inline de `AccountPage` bajo el proceso de
@@ -46056,15 +46076,15 @@ class OrgInfoSheetComponent {
46056
46076
  text: this.closeProps.text || this.resolvedCloseLabel(),
46057
46077
  }));
46058
46078
  addIcons({ peopleOutline, swapHorizontalOutline, mailOutline });
46059
- if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$a)) {
46060
- this.i18n.registerContent(DEFAULT_NAMESPACE$a, ORG_INFO_SHEET_I18N);
46079
+ if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$c)) {
46080
+ this.i18n.registerContent(DEFAULT_NAMESPACE$c, ORG_INFO_SHEET_I18N);
46061
46081
  }
46062
46082
  }
46063
46083
  dismiss() {
46064
46084
  this._modalRef?.dismiss(null, 'cancel');
46065
46085
  }
46066
46086
  tt(key) {
46067
- return this.i18n.t(key, DEFAULT_NAMESPACE$a);
46087
+ return this.i18n.t(key, DEFAULT_NAMESPACE$c);
46068
46088
  }
46069
46089
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: OrgInfoSheetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
46070
46090
  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: `
@@ -46231,7 +46251,7 @@ const SWITCH_ORG_MODAL_I18N = {
46231
46251
  },
46232
46252
  };
46233
46253
 
46234
- const DEFAULT_NAMESPACE$9 = 'Settings.SwitchOrg';
46254
+ const DEFAULT_NAMESPACE$b = 'Settings.SwitchOrg';
46235
46255
  addIcons({ businessOutline, checkmarkCircleOutline });
46236
46256
  /**
46237
46257
  * `val-switch-org-modal` — modal de cambio de organización activa (organism
@@ -46257,7 +46277,7 @@ class SwitchOrgModalComponent {
46257
46277
  this.orgSwitch = inject(OrgSwitchService);
46258
46278
  this.errors = inject(ValtechErrorService);
46259
46279
  /** Namespace i18n con que la vista resuelve sus textos. */
46260
- this.i18nNamespace = DEFAULT_NAMESPACE$9;
46280
+ this.i18nNamespace = DEFAULT_NAMESPACE$b;
46261
46281
  this.orgs = signal([]);
46262
46282
  this.loading = signal(false);
46263
46283
  this.query = signal('');
@@ -46273,8 +46293,8 @@ class SwitchOrgModalComponent {
46273
46293
  return list;
46274
46294
  return list.filter(o => o.name.toLowerCase().includes(q));
46275
46295
  });
46276
- if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$9)) {
46277
- this.i18n.registerContent(DEFAULT_NAMESPACE$9, SWITCH_ORG_MODAL_I18N);
46296
+ if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$b)) {
46297
+ this.i18n.registerContent(DEFAULT_NAMESPACE$b, SWITCH_ORG_MODAL_I18N);
46278
46298
  }
46279
46299
  this.loadOrgs();
46280
46300
  }
@@ -46592,7 +46612,7 @@ const ACCOUNT_VIEW_I18N = {
46592
46612
  };
46593
46613
 
46594
46614
  addIcons({ peopleOutline, businessOutline, chevronForwardOutline, swapHorizontalOutline, mailOutline });
46595
- const DEFAULT_NAMESPACE$8 = 'Settings.Account';
46615
+ const DEFAULT_NAMESPACE$a = 'Settings.Account';
46596
46616
  const DEFAULT_MANAGE_ORG_ROUTE = '/app/settings/organization';
46597
46617
  /**
46598
46618
  * `val-account-view` — vista Cuenta full-feature autocontenida (organism).
@@ -46633,7 +46653,7 @@ class AccountViewComponent {
46633
46653
  showNewOrgCta: merged.showNewOrgCta ?? true,
46634
46654
  showLogout: merged.showLogout ?? true,
46635
46655
  showDeleteAccount: merged.showDeleteAccount ?? true,
46636
- i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$8,
46656
+ i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$a,
46637
46657
  manageOrgRoute: merged.manageOrgRoute ?? DEFAULT_MANAGE_ORG_ROUTE,
46638
46658
  onOrgSwitched: merged.onOrgSwitched,
46639
46659
  onOrgCreated: merged.onOrgCreated,
@@ -47358,7 +47378,7 @@ const EDIT_ORG_MODAL_I18N = {
47358
47378
  },
47359
47379
  };
47360
47380
 
47361
- const DEFAULT_NAMESPACE$7 = 'EditOrgModal';
47381
+ const DEFAULT_NAMESPACE$9 = 'EditOrgModal';
47362
47382
  /**
47363
47383
  * `val-edit-org-modal` — modal de edición de organización (nombre + descripción).
47364
47384
  * Promovido desde `showcase` bajo el proceso de ADR-021. Reusado por la vista de
@@ -47385,10 +47405,10 @@ class EditOrgModalComponent {
47385
47405
  /** Organización a editar (pasada por `componentProps`). */
47386
47406
  this.org = null;
47387
47407
  /** Namespace i18n con que la vista resuelve sus textos. */
47388
- this.i18nNamespace = DEFAULT_NAMESPACE$7;
47408
+ this.i18nNamespace = DEFAULT_NAMESPACE$9;
47389
47409
  this.saving = signal(false);
47390
- if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$7)) {
47391
- this.i18n.registerContent(DEFAULT_NAMESPACE$7, EDIT_ORG_MODAL_I18N);
47410
+ if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$9)) {
47411
+ this.i18n.registerContent(DEFAULT_NAMESPACE$9, EDIT_ORG_MODAL_I18N);
47392
47412
  }
47393
47413
  this.formMeta = signal(this.buildFormMeta());
47394
47414
  effect(() => {
@@ -47626,7 +47646,7 @@ const INVITE_MEMBER_MODAL_I18N = {
47626
47646
  },
47627
47647
  };
47628
47648
 
47629
- const DEFAULT_NAMESPACE$6 = 'InviteModal';
47649
+ const DEFAULT_NAMESPACE$8 = 'InviteModal';
47630
47650
  addIcons({ personOutline, mailOutline, closeOutline, checkmarkCircleOutline });
47631
47651
  /**
47632
47652
  * `val-invite-member-modal` — invitación de miembros a una organización.
@@ -47657,7 +47677,7 @@ class InviteMemberModalComponent {
47657
47677
  /** Ids de miembros ya existentes (excluidos de los resultados). */
47658
47678
  this.existingMemberIds = [];
47659
47679
  /** Namespace i18n con que la vista resuelve sus textos. */
47660
- this.i18nNamespace = DEFAULT_NAMESPACE$6;
47680
+ this.i18nNamespace = DEFAULT_NAMESPACE$8;
47661
47681
  this.query = signal('');
47662
47682
  this.searching = signal(false);
47663
47683
  this.searchResults = signal([]);
@@ -47704,8 +47724,8 @@ class InviteMemberModalComponent {
47704
47724
  state: this.sending() ? ComponentStates.WORKING : ComponentStates.ENABLED,
47705
47725
  }));
47706
47726
  this.searchSubject = new Subject();
47707
- if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$6)) {
47708
- this.i18n.registerContent(DEFAULT_NAMESPACE$6, INVITE_MEMBER_MODAL_I18N);
47727
+ if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$8)) {
47728
+ this.i18n.registerContent(DEFAULT_NAMESPACE$8, INVITE_MEMBER_MODAL_I18N);
47709
47729
  }
47710
47730
  this.loadRoles();
47711
47731
  this.roleSub = this.roleControl.valueChanges.subscribe(v => {
@@ -48194,7 +48214,7 @@ const MEMBER_DETAIL_MODAL_I18N = {
48194
48214
  },
48195
48215
  };
48196
48216
 
48197
- const DEFAULT_NAMESPACE$5 = 'Settings.MemberDetail';
48217
+ const DEFAULT_NAMESPACE$7 = 'Settings.MemberDetail';
48198
48218
  /**
48199
48219
  * `val-member-detail-modal` — detalle de miembro de organización: perfil, rol y
48200
48220
  * permisos. Promovido desde `showcase` bajo el proceso de ADR-021.
@@ -48231,7 +48251,7 @@ class MemberDetailModalComponent {
48231
48251
  { id: 'admin', name: 'admin' },
48232
48252
  ];
48233
48253
  /** Namespace i18n con que la vista resuelve sus textos. */
48234
- this.i18nNamespace = DEFAULT_NAMESPACE$5;
48254
+ this.i18nNamespace = DEFAULT_NAMESPACE$7;
48235
48255
  this.working = signal(false);
48236
48256
  this.currentRole = signal('');
48237
48257
  this.roleControl = new FormControl('');
@@ -48271,8 +48291,8 @@ class MemberDetailModalComponent {
48271
48291
  type: 'button',
48272
48292
  state: this.working() ? ComponentStates.WORKING : ComponentStates.ENABLED,
48273
48293
  }));
48274
- if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$5)) {
48275
- this.i18n.registerContent(DEFAULT_NAMESPACE$5, MEMBER_DETAIL_MODAL_I18N);
48294
+ if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$7)) {
48295
+ this.i18n.registerContent(DEFAULT_NAMESPACE$7, MEMBER_DETAIL_MODAL_I18N);
48276
48296
  }
48277
48297
  }
48278
48298
  ngOnInit() {
@@ -48609,7 +48629,7 @@ const TRANSFER_OWNERSHIP_MODAL_I18N = {
48609
48629
  },
48610
48630
  };
48611
48631
 
48612
- const DEFAULT_NAMESPACE$4 = 'Settings.TransferModal';
48632
+ const DEFAULT_NAMESPACE$6 = 'Settings.TransferModal';
48613
48633
  /**
48614
48634
  * `val-transfer-ownership-modal` — selector de miembro para transferir la
48615
48635
  * propiedad de la organización. Promovido desde `showcase` bajo el proceso de
@@ -48633,7 +48653,7 @@ class TransferOwnershipModalComponent {
48633
48653
  /** Lista de miembros transferibles (todos excepto el owner actual). */
48634
48654
  this.members = [];
48635
48655
  /** Namespace i18n con que la vista resuelve sus textos. */
48636
- this.i18nNamespace = DEFAULT_NAMESPACE$4;
48656
+ this.i18nNamespace = DEFAULT_NAMESPACE$6;
48637
48657
  this.query = signal('');
48638
48658
  this.selectedId = signal('');
48639
48659
  this.filtered = computed(() => {
@@ -48645,8 +48665,8 @@ class TransferOwnershipModalComponent {
48645
48665
  m.userId.toLowerCase().includes(q));
48646
48666
  });
48647
48667
  this.noResultsLabel = computed(() => `${this.t('noResults')} "${this.query()}"`);
48648
- if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$4)) {
48649
- this.i18n.registerContent(DEFAULT_NAMESPACE$4, TRANSFER_OWNERSHIP_MODAL_I18N);
48668
+ if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$6)) {
48669
+ this.i18n.registerContent(DEFAULT_NAMESPACE$6, TRANSFER_OWNERSHIP_MODAL_I18N);
48650
48670
  }
48651
48671
  }
48652
48672
  ngOnInit() {
@@ -48831,6 +48851,849 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
48831
48851
  type: Input
48832
48852
  }] } });
48833
48853
 
48854
+ /**
48855
+ * Defaults i18n (es/en) embebidos en `val-member-import-modal` (ADR-023).
48856
+ * Auto-registrados si el consumer no proveyó el namespace `Settings.ImportModal`.
48857
+ */
48858
+ const MEMBER_IMPORT_MODAL_I18N = {
48859
+ es: {
48860
+ title: 'Importar miembros',
48861
+ hint: 'Pega un CSV con columnas: email, nombre, rol (una fila por persona).',
48862
+ csvPlaceholder: 'email,nombre,rol\nana@x.com,Ana,editor\nbeto@x.com,Beto,viewer',
48863
+ rolesAvailable: 'Roles disponibles:',
48864
+ onConflictLabel: 'Si el email ya existe',
48865
+ conflictAssign: 'Asignar el rol',
48866
+ conflictSkip: 'Omitir',
48867
+ conflictError: 'Marcar error',
48868
+ sendActivation: 'Enviar email de activación a las cuentas nuevas',
48869
+ submit: 'Importar',
48870
+ submitting: 'Importando...',
48871
+ parseError: 'No se pudo interpretar el CSV. Revisa el formato.',
48872
+ noRows: 'Pega al menos una fila válida (email y rol).',
48873
+ summary: 'Resumen',
48874
+ created: 'creados',
48875
+ assigned: 'asignados',
48876
+ skipped: 'omitidos',
48877
+ failed: 'con error',
48878
+ colEmail: 'Email',
48879
+ colStatus: 'Estado',
48880
+ colDetail: 'Detalle',
48881
+ activationSent: 'activación enviada',
48882
+ activationPending: 'activación pendiente',
48883
+ again: 'Importar otro lote',
48884
+ close: 'Cerrar',
48885
+ genericError: 'No se pudo completar la importación. Intenta de nuevo.',
48886
+ },
48887
+ en: {
48888
+ title: 'Import members',
48889
+ hint: 'Paste a CSV with columns: email, name, role (one row per person).',
48890
+ csvPlaceholder: 'email,name,role\nana@x.com,Ana,editor\nbeto@x.com,Beto,viewer',
48891
+ rolesAvailable: 'Available roles:',
48892
+ onConflictLabel: 'If the email already exists',
48893
+ conflictAssign: 'Assign the role',
48894
+ conflictSkip: 'Skip',
48895
+ conflictError: 'Mark as error',
48896
+ sendActivation: 'Send activation email to new accounts',
48897
+ submit: 'Import',
48898
+ submitting: 'Importing...',
48899
+ parseError: "Couldn't parse the CSV. Check the format.",
48900
+ noRows: 'Paste at least one valid row (email and role).',
48901
+ summary: 'Summary',
48902
+ created: 'created',
48903
+ assigned: 'assigned',
48904
+ skipped: 'skipped',
48905
+ failed: 'failed',
48906
+ colEmail: 'Email',
48907
+ colStatus: 'Status',
48908
+ colDetail: 'Detail',
48909
+ activationSent: 'activation sent',
48910
+ activationPending: 'activation pending',
48911
+ again: 'Import another batch',
48912
+ close: 'Close',
48913
+ genericError: "Couldn't complete the import. Try again.",
48914
+ },
48915
+ };
48916
+
48917
+ const DEFAULT_NAMESPACE$5 = 'Settings.ImportModal';
48918
+ /**
48919
+ * `val-member-import-modal` — panel de carga masiva de miembros (ADR-023 fase 6).
48920
+ *
48921
+ * El admin pega un CSV (`email,nombre,rol`); el modal lo parsea a filas y llama
48922
+ * `OrgService.importMembers`. Muestra el resultado por-fila (partial success):
48923
+ * `created` / `assigned` / `skipped` / `error`. Header canónico (Regla #5).
48924
+ *
48925
+ * Auto-registra defaults i18n (es/en) en `Settings.ImportModal`.
48926
+ */
48927
+ class MemberImportModalComponent {
48928
+ constructor() {
48929
+ this.i18n = inject(I18nService);
48930
+ this.orgService = inject(OrgService);
48931
+ /** Org destino del import. */
48932
+ this.orgId = '';
48933
+ /** Nombres de roles disponibles (para el hint). Opcional. */
48934
+ this.availableRoles = [];
48935
+ /** Namespace i18n. */
48936
+ this.i18nNamespace = DEFAULT_NAMESPACE$5;
48937
+ this.csv = '';
48938
+ this.onConflict = 'assignRole';
48939
+ this.sendActivation = true;
48940
+ this.submitting = signal(false);
48941
+ this.errorMsg = signal('');
48942
+ this.results = signal(null);
48943
+ this.rolesHint = computed(() => this.availableRoles.length ? `${this.t('rolesAvailable')} ${this.availableRoles.join(', ')}` : '');
48944
+ this.summaryLabel = computed(() => {
48945
+ const s = this.results()?.summary;
48946
+ if (!s)
48947
+ return this.t('summary');
48948
+ return `${this.t('summary')}: ${s.created} ${this.t('created')} · ${s.assigned} ${this.t('assigned')} · ${s.skipped} ${this.t('skipped')} · ${s.failed} ${this.t('failed')}`;
48949
+ });
48950
+ if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$5)) {
48951
+ this.i18n.registerContent(DEFAULT_NAMESPACE$5, MEMBER_IMPORT_MODAL_I18N);
48952
+ }
48953
+ }
48954
+ ngOnInit() {
48955
+ if (!this.i18n.hasNamespace(this.i18nNamespace)) {
48956
+ this.i18n.registerContent(this.i18nNamespace, MEMBER_IMPORT_MODAL_I18N);
48957
+ }
48958
+ }
48959
+ /** Parsea el CSV pegado a filas {email,name,roleName}. Tolera header. */
48960
+ parseCsv() {
48961
+ const rows = [];
48962
+ for (const raw of this.csv.split(/\r?\n/)) {
48963
+ const line = raw.trim();
48964
+ if (!line)
48965
+ continue;
48966
+ const cols = line.split(',').map(c => c.trim());
48967
+ const email = (cols[0] ?? '').toLowerCase();
48968
+ // Saltar header (primera col "email") o líneas sin @.
48969
+ if (!email || !email.includes('@'))
48970
+ continue;
48971
+ rows.push({ email, name: cols[1] ?? '', roleName: cols[2] ?? '' });
48972
+ }
48973
+ return rows;
48974
+ }
48975
+ submit() {
48976
+ this.errorMsg.set('');
48977
+ const users = this.parseCsv();
48978
+ if (users.length === 0) {
48979
+ this.errorMsg.set(this.t('noRows'));
48980
+ return;
48981
+ }
48982
+ if (users.some(u => !u.roleName)) {
48983
+ this.errorMsg.set(this.t('parseError'));
48984
+ return;
48985
+ }
48986
+ this.submitting.set(true);
48987
+ this.orgService
48988
+ .importMembers(this.orgId, {
48989
+ users,
48990
+ onConflict: this.onConflict,
48991
+ sendActivation: this.sendActivation,
48992
+ })
48993
+ .subscribe({
48994
+ next: res => {
48995
+ this.submitting.set(false);
48996
+ this.results.set(res);
48997
+ // Refrescar lista del consumer si hubo altas/asignaciones efectivas.
48998
+ if (res.summary.created + res.summary.assigned > 0) {
48999
+ this.onSuccess?.();
49000
+ }
49001
+ },
49002
+ error: err => {
49003
+ this.submitting.set(false);
49004
+ this.errorMsg.set(err?.error?.message ?? this.t('genericError'));
49005
+ },
49006
+ });
49007
+ }
49008
+ statusLabel(r) {
49009
+ const base = this.t(r.status === 'error' ? 'failed' : r.status);
49010
+ if (r.status === 'created' && r.activation) {
49011
+ return `${base} · ${this.t(r.activation === 'sent' ? 'activationSent' : 'activationPending')}`;
49012
+ }
49013
+ return base;
49014
+ }
49015
+ reset() {
49016
+ this.csv = '';
49017
+ this.errorMsg.set('');
49018
+ this.results.set(null);
49019
+ }
49020
+ dismiss() {
49021
+ this._modalRef?.dismiss(null, 'cancel');
49022
+ }
49023
+ t(key) {
49024
+ return this.i18n.t(key, this.i18nNamespace);
49025
+ }
49026
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MemberImportModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
49027
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: MemberImportModalComponent, isStandalone: true, selector: "val-member-import-modal", inputs: { orgId: "orgId", availableRoles: "availableRoles", onSuccess: "onSuccess", _modalRef: "_modalRef", i18nNamespace: "i18nNamespace" }, ngImport: i0, template: `
49028
+ <ion-header>
49029
+ <ion-toolbar>
49030
+ <ion-buttons slot="end">
49031
+ <ion-button fill="clear" color="dark" (click)="dismiss()">
49032
+ <strong>{{ t('close') }}</strong>
49033
+ </ion-button>
49034
+ </ion-buttons>
49035
+ </ion-toolbar>
49036
+ </ion-header>
49037
+
49038
+ <ion-content class="ion-padding">
49039
+ <val-display [props]="{ content: t('title'), size: 'small', color: 'dark' }" />
49040
+
49041
+ @if (!results()) {
49042
+ <val-title [props]="{ content: t('hint'), size: 'large', color: '', bold: false }" />
49043
+
49044
+ @if (rolesHint()) {
49045
+ <val-text [props]="{ content: rolesHint(), size: 'small', color: 'medium', bold: false }" />
49046
+ }
49047
+
49048
+ <ion-textarea
49049
+ class="import-csv"
49050
+ [autoGrow]="true"
49051
+ [rows]="6"
49052
+ [placeholder]="t('csvPlaceholder')"
49053
+ [(ngModel)]="csv"
49054
+ />
49055
+
49056
+ <ion-select
49057
+ class="import-conflict"
49058
+ label="{{ t('onConflictLabel') }}"
49059
+ labelPlacement="stacked"
49060
+ interface="popover"
49061
+ [(ngModel)]="onConflict"
49062
+ >
49063
+ <ion-select-option value="assignRole">{{ t('conflictAssign') }}</ion-select-option>
49064
+ <ion-select-option value="skip">{{ t('conflictSkip') }}</ion-select-option>
49065
+ <ion-select-option value="error">{{ t('conflictError') }}</ion-select-option>
49066
+ </ion-select>
49067
+
49068
+ <ion-checkbox class="import-activation" labelPlacement="end" [(ngModel)]="sendActivation">
49069
+ {{ t('sendActivation') }}
49070
+ </ion-checkbox>
49071
+
49072
+ @if (errorMsg()) {
49073
+ <p class="import-error">{{ errorMsg() }}</p>
49074
+ }
49075
+
49076
+ <div class="import-actions">
49077
+ <ion-button expand="block" color="dark" [disabled]="submitting()" (click)="submit()">
49078
+ @if (submitting()) {
49079
+ <ion-spinner name="dots" />
49080
+ &nbsp;{{ t('submitting') }}
49081
+ } @else {
49082
+ {{ t('submit') }}
49083
+ }
49084
+ </ion-button>
49085
+ </div>
49086
+ } @else {
49087
+ <!-- Resultados -->
49088
+ <val-title [props]="{ content: summaryLabel(), size: 'large', color: '', bold: true }" />
49089
+ <div class="import-results">
49090
+ @for (r of results()!.results; track r.email) {
49091
+ <div class="import-row import-row--{{ r.status }}">
49092
+ <span class="import-row__email">{{ r.email }}</span>
49093
+ <span class="import-row__status">{{ statusLabel(r) }}</span>
49094
+ @if (r.reason) {
49095
+ <span class="import-row__reason">{{ r.reason }}</span>
49096
+ }
49097
+ </div>
49098
+ }
49099
+ </div>
49100
+ <div class="import-actions">
49101
+ <ion-button expand="block" fill="outline" color="dark" (click)="reset()">
49102
+ {{ t('again') }}
49103
+ </ion-button>
49104
+ </div>
49105
+ }
49106
+ </ion-content>
49107
+ `, isInline: true, styles: ["val-display{display:block;margin-bottom:4px}val-title{display:block;margin-bottom:12px}.import-csv{--background: var(--ion-color-light);border-radius:10px;margin-bottom:16px;font-family:monospace;font-size:13px}.import-conflict{margin-bottom:12px}.import-activation{display:block;margin-bottom:16px;font-size:14px}.import-error{margin:0 0 12px;font-size:.875rem;color:var(--ion-color-danger, #c0392b)}.import-actions{margin-top:8px}.import-results{display:flex;flex-direction:column;gap:6px;margin:12px 0}.import-row{display:grid;grid-template-columns:1fr auto;grid-row-gap:2px;align-items:center;padding:10px 12px;border-radius:8px;border-left:3px solid var(--ion-color-medium);background:var(--ion-color-light);font-size:14px}.import-row--created{border-left-color:var(--ion-color-success, #2dd36f)}.import-row--assigned{border-left-color:var(--ion-color-primary, #7026df)}.import-row--skipped{border-left-color:var(--ion-color-medium, #92949c)}.import-row--error{border-left-color:var(--ion-color-danger, #c0392b)}.import-row__email{font-weight:600;color:var(--ion-color-dark)}.import-row__status{text-align:right;color:var(--ion-color-medium-shade, #6b6e76)}.import-row__reason{grid-column:1 / -1;font-size:12px;color:var(--ion-color-danger, #c0392b)}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$7.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$7.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { 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: IonTextarea, selector: "ion-textarea", inputs: ["autoGrow", "autocapitalize", "autofocus", "clearOnEdit", "color", "cols", "counter", "counterFormatter", "debounce", "disabled", "enterkeyhint", "errorText", "fill", "helperText", "inputmode", "label", "labelPlacement", "maxlength", "minlength", "mode", "name", "placeholder", "readonly", "required", "rows", "shape", "spellcheck", "value", "wrap"] }, { kind: "component", type: IonSelect, selector: "ion-select", inputs: ["cancelText", "color", "compareWith", "disabled", "errorText", "expandedIcon", "fill", "helperText", "interface", "interfaceOptions", "justify", "label", "labelPlacement", "mode", "multiple", "name", "okText", "placeholder", "selectedText", "shape", "toggleIcon", "value"] }, { kind: "component", type: IonSelectOption, selector: "ion-select-option", inputs: ["disabled", "value"] }, { kind: "component", type: IonCheckbox, selector: "ion-checkbox", inputs: ["checked", "color", "disabled", "errorText", "helperText", "indeterminate", "justify", "labelPlacement", "mode", "name", "value"] }, { kind: "component", type: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { kind: "component", type: DisplayComponent, selector: "val-display", inputs: ["props"] }, { kind: "component", type: TextComponent, selector: "val-text", inputs: ["props"] }, { kind: "component", type: TitleComponent, selector: "val-title", inputs: ["props"] }] }); }
49108
+ }
49109
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MemberImportModalComponent, decorators: [{
49110
+ type: Component,
49111
+ args: [{ selector: 'val-member-import-modal', standalone: true, imports: [
49112
+ FormsModule,
49113
+ IonHeader,
49114
+ IonToolbar,
49115
+ IonButtons,
49116
+ IonButton,
49117
+ IonContent,
49118
+ IonTextarea,
49119
+ IonSelect,
49120
+ IonSelectOption,
49121
+ IonCheckbox,
49122
+ IonSpinner,
49123
+ DisplayComponent,
49124
+ TextComponent,
49125
+ TitleComponent,
49126
+ ], template: `
49127
+ <ion-header>
49128
+ <ion-toolbar>
49129
+ <ion-buttons slot="end">
49130
+ <ion-button fill="clear" color="dark" (click)="dismiss()">
49131
+ <strong>{{ t('close') }}</strong>
49132
+ </ion-button>
49133
+ </ion-buttons>
49134
+ </ion-toolbar>
49135
+ </ion-header>
49136
+
49137
+ <ion-content class="ion-padding">
49138
+ <val-display [props]="{ content: t('title'), size: 'small', color: 'dark' }" />
49139
+
49140
+ @if (!results()) {
49141
+ <val-title [props]="{ content: t('hint'), size: 'large', color: '', bold: false }" />
49142
+
49143
+ @if (rolesHint()) {
49144
+ <val-text [props]="{ content: rolesHint(), size: 'small', color: 'medium', bold: false }" />
49145
+ }
49146
+
49147
+ <ion-textarea
49148
+ class="import-csv"
49149
+ [autoGrow]="true"
49150
+ [rows]="6"
49151
+ [placeholder]="t('csvPlaceholder')"
49152
+ [(ngModel)]="csv"
49153
+ />
49154
+
49155
+ <ion-select
49156
+ class="import-conflict"
49157
+ label="{{ t('onConflictLabel') }}"
49158
+ labelPlacement="stacked"
49159
+ interface="popover"
49160
+ [(ngModel)]="onConflict"
49161
+ >
49162
+ <ion-select-option value="assignRole">{{ t('conflictAssign') }}</ion-select-option>
49163
+ <ion-select-option value="skip">{{ t('conflictSkip') }}</ion-select-option>
49164
+ <ion-select-option value="error">{{ t('conflictError') }}</ion-select-option>
49165
+ </ion-select>
49166
+
49167
+ <ion-checkbox class="import-activation" labelPlacement="end" [(ngModel)]="sendActivation">
49168
+ {{ t('sendActivation') }}
49169
+ </ion-checkbox>
49170
+
49171
+ @if (errorMsg()) {
49172
+ <p class="import-error">{{ errorMsg() }}</p>
49173
+ }
49174
+
49175
+ <div class="import-actions">
49176
+ <ion-button expand="block" color="dark" [disabled]="submitting()" (click)="submit()">
49177
+ @if (submitting()) {
49178
+ <ion-spinner name="dots" />
49179
+ &nbsp;{{ t('submitting') }}
49180
+ } @else {
49181
+ {{ t('submit') }}
49182
+ }
49183
+ </ion-button>
49184
+ </div>
49185
+ } @else {
49186
+ <!-- Resultados -->
49187
+ <val-title [props]="{ content: summaryLabel(), size: 'large', color: '', bold: true }" />
49188
+ <div class="import-results">
49189
+ @for (r of results()!.results; track r.email) {
49190
+ <div class="import-row import-row--{{ r.status }}">
49191
+ <span class="import-row__email">{{ r.email }}</span>
49192
+ <span class="import-row__status">{{ statusLabel(r) }}</span>
49193
+ @if (r.reason) {
49194
+ <span class="import-row__reason">{{ r.reason }}</span>
49195
+ }
49196
+ </div>
49197
+ }
49198
+ </div>
49199
+ <div class="import-actions">
49200
+ <ion-button expand="block" fill="outline" color="dark" (click)="reset()">
49201
+ {{ t('again') }}
49202
+ </ion-button>
49203
+ </div>
49204
+ }
49205
+ </ion-content>
49206
+ `, styles: ["val-display{display:block;margin-bottom:4px}val-title{display:block;margin-bottom:12px}.import-csv{--background: var(--ion-color-light);border-radius:10px;margin-bottom:16px;font-family:monospace;font-size:13px}.import-conflict{margin-bottom:12px}.import-activation{display:block;margin-bottom:16px;font-size:14px}.import-error{margin:0 0 12px;font-size:.875rem;color:var(--ion-color-danger, #c0392b)}.import-actions{margin-top:8px}.import-results{display:flex;flex-direction:column;gap:6px;margin:12px 0}.import-row{display:grid;grid-template-columns:1fr auto;grid-row-gap:2px;align-items:center;padding:10px 12px;border-radius:8px;border-left:3px solid var(--ion-color-medium);background:var(--ion-color-light);font-size:14px}.import-row--created{border-left-color:var(--ion-color-success, #2dd36f)}.import-row--assigned{border-left-color:var(--ion-color-primary, #7026df)}.import-row--skipped{border-left-color:var(--ion-color-medium, #92949c)}.import-row--error{border-left-color:var(--ion-color-danger, #c0392b)}.import-row__email{font-weight:600;color:var(--ion-color-dark)}.import-row__status{text-align:right;color:var(--ion-color-medium-shade, #6b6e76)}.import-row__reason{grid-column:1 / -1;font-size:12px;color:var(--ion-color-danger, #c0392b)}\n"] }]
49207
+ }], ctorParameters: () => [], propDecorators: { orgId: [{
49208
+ type: Input
49209
+ }], availableRoles: [{
49210
+ type: Input
49211
+ }], onSuccess: [{
49212
+ type: Input
49213
+ }], _modalRef: [{
49214
+ type: Input
49215
+ }], i18nNamespace: [{
49216
+ type: Input
49217
+ }] } });
49218
+
49219
+ /**
49220
+ * Cliente HTTP de Client API Keys org-scopeadas (ADR-023). La org activa se
49221
+ * resuelve en backend desde el JWT, así que el path no la incluye.
49222
+ */
49223
+ class ApiKeyService {
49224
+ constructor(config, http) {
49225
+ this.config = config;
49226
+ this.http = http;
49227
+ }
49228
+ get baseUrl() {
49229
+ return `${this.config.apiUrl}/org/api-keys`;
49230
+ }
49231
+ /** Lista las API keys de la org activa. */
49232
+ list() {
49233
+ return this.http.get(`${this.baseUrl}/`).pipe(map$1(r => r.keys ?? []));
49234
+ }
49235
+ /** Crea una API key. La respuesta incluye el secreto (se muestra UNA vez). */
49236
+ create(req) {
49237
+ return this.http.post(`${this.baseUrl}/`, req);
49238
+ }
49239
+ /** Revoca (elimina) una API key por id. */
49240
+ revoke(keyId) {
49241
+ return this.http.delete(`${this.baseUrl}/${keyId}`);
49242
+ }
49243
+ /** Catálogo de permisos asignables a una key. */
49244
+ availablePermissions() {
49245
+ return this.http
49246
+ .get(`${this.baseUrl}/permissions`)
49247
+ .pipe(map$1(r => r.permissions ?? []));
49248
+ }
49249
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ApiKeyService, deps: [{ token: VALTECH_AUTH_CONFIG }, { token: i1$3.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable }); }
49250
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ApiKeyService, providedIn: 'root' }); }
49251
+ }
49252
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ApiKeyService, decorators: [{
49253
+ type: Injectable,
49254
+ args: [{ providedIn: 'root' }]
49255
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
49256
+ type: Inject,
49257
+ args: [VALTECH_AUTH_CONFIG]
49258
+ }] }, { type: i1$3.HttpClient }] });
49259
+
49260
+ /**
49261
+ * Defaults i18n (es/en) embebidos en `val-api-keys-modal` (ADR-023).
49262
+ * Auto-registrados si el consumer no proveyó el namespace `Settings.ApiKeysModal`.
49263
+ */
49264
+ const API_KEYS_MODAL_I18N = {
49265
+ es: {
49266
+ title: 'Client API Keys',
49267
+ hint: 'Llaves M2M org-scopeadas para integraciones (p. ej. importar miembros).',
49268
+ empty: 'Aún no hay API keys en esta organización.',
49269
+ newKey: 'Nueva API key',
49270
+ nameLabel: 'Nombre',
49271
+ namePlaceholder: 'Ej: Integración RRHH',
49272
+ permissionsLabel: 'Permisos',
49273
+ expiresLabel: 'Expira en (días, opcional)',
49274
+ create: 'Crear key',
49275
+ creating: 'Creando...',
49276
+ cancel: 'Cancelar',
49277
+ revoke: 'Revocar',
49278
+ revokeConfirm: '¿Revocar esta API key? Las integraciones que la usen dejarán de funcionar.',
49279
+ secretTitle: 'Copia tu API key ahora',
49280
+ secretWarning: 'Este secreto se muestra una sola vez. Guárdalo en un lugar seguro.',
49281
+ copy: 'Copiar',
49282
+ copied: 'Copiada',
49283
+ done: 'Listo',
49284
+ statusActive: 'activa',
49285
+ lastUsed: 'Último uso',
49286
+ never: 'nunca',
49287
+ permsCount: 'permisos',
49288
+ noName: 'El nombre es obligatorio.',
49289
+ noPerms: 'Selecciona al menos un permiso.',
49290
+ close: 'Cerrar',
49291
+ genericError: 'No se pudo completar la operación. Intenta de nuevo.',
49292
+ },
49293
+ en: {
49294
+ title: 'Client API Keys',
49295
+ hint: 'Org-scoped M2M keys for integrations (e.g. member import).',
49296
+ empty: 'No API keys in this organization yet.',
49297
+ newKey: 'New API key',
49298
+ nameLabel: 'Name',
49299
+ namePlaceholder: 'E.g: HR integration',
49300
+ permissionsLabel: 'Permissions',
49301
+ expiresLabel: 'Expires in (days, optional)',
49302
+ create: 'Create key',
49303
+ creating: 'Creating...',
49304
+ cancel: 'Cancel',
49305
+ revoke: 'Revoke',
49306
+ revokeConfirm: 'Revoke this API key? Integrations using it will stop working.',
49307
+ secretTitle: 'Copy your API key now',
49308
+ secretWarning: 'This secret is shown only once. Store it somewhere safe.',
49309
+ copy: 'Copy',
49310
+ copied: 'Copied',
49311
+ done: 'Done',
49312
+ statusActive: 'active',
49313
+ lastUsed: 'Last used',
49314
+ never: 'never',
49315
+ permsCount: 'permissions',
49316
+ noName: 'Name is required.',
49317
+ noPerms: 'Select at least one permission.',
49318
+ close: 'Close',
49319
+ genericError: "Couldn't complete the operation. Try again.",
49320
+ },
49321
+ };
49322
+
49323
+ const DEFAULT_NAMESPACE$4 = 'Settings.ApiKeysModal';
49324
+ /**
49325
+ * `val-api-keys-modal` — gestión de Client API Keys org-scopeadas (ADR-023 fase 6).
49326
+ *
49327
+ * Lista las keys de la org, permite crear (mostrando el secreto UNA vez con copia)
49328
+ * y revocar. Pensado para habilitar integraciones M2M del import de miembros, pero
49329
+ * sirve a cualquier permiso del catálogo. Header canónico (Regla #5).
49330
+ */
49331
+ class ApiKeysModalComponent {
49332
+ constructor() {
49333
+ this.i18n = inject(I18nService);
49334
+ this.apiKeys = inject(ApiKeyService);
49335
+ /** Namespace i18n. */
49336
+ this.i18nNamespace = DEFAULT_NAMESPACE$4;
49337
+ this.keys = signal([]);
49338
+ this.availablePermissions = signal([]);
49339
+ this.loading = signal(true);
49340
+ this.showCreate = signal(false);
49341
+ this.submitting = signal(false);
49342
+ this.errorMsg = signal('');
49343
+ this.secret = signal(null);
49344
+ this.copied = signal(false);
49345
+ this.name = '';
49346
+ this.expiresInDays = null;
49347
+ this.selectedPerms = signal(new Set());
49348
+ if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$4)) {
49349
+ this.i18n.registerContent(DEFAULT_NAMESPACE$4, API_KEYS_MODAL_I18N);
49350
+ }
49351
+ }
49352
+ ngOnInit() {
49353
+ if (!this.i18n.hasNamespace(this.i18nNamespace)) {
49354
+ this.i18n.registerContent(this.i18nNamespace, API_KEYS_MODAL_I18N);
49355
+ }
49356
+ this.refresh();
49357
+ this.apiKeys.availablePermissions().subscribe({
49358
+ next: perms => this.availablePermissions.set(perms),
49359
+ error: () => { },
49360
+ });
49361
+ }
49362
+ refresh() {
49363
+ this.loading.set(true);
49364
+ this.apiKeys.list().subscribe({
49365
+ next: keys => {
49366
+ this.keys.set(keys);
49367
+ this.loading.set(false);
49368
+ },
49369
+ error: () => this.loading.set(false),
49370
+ });
49371
+ }
49372
+ openCreate() {
49373
+ this.errorMsg.set('');
49374
+ this.name = '';
49375
+ this.expiresInDays = null;
49376
+ this.selectedPerms.set(new Set());
49377
+ this.showCreate.set(true);
49378
+ }
49379
+ togglePerm(perm, ev) {
49380
+ const checked = ev.detail?.checked ?? false;
49381
+ const next = new Set(this.selectedPerms());
49382
+ if (checked) {
49383
+ next.add(perm);
49384
+ }
49385
+ else {
49386
+ next.delete(perm);
49387
+ }
49388
+ this.selectedPerms.set(next);
49389
+ }
49390
+ submit() {
49391
+ this.errorMsg.set('');
49392
+ if (!this.name.trim()) {
49393
+ this.errorMsg.set(this.t('noName'));
49394
+ return;
49395
+ }
49396
+ const permissions = Array.from(this.selectedPerms());
49397
+ if (permissions.length === 0) {
49398
+ this.errorMsg.set(this.t('noPerms'));
49399
+ return;
49400
+ }
49401
+ this.submitting.set(true);
49402
+ this.apiKeys
49403
+ .create({
49404
+ name: this.name.trim(),
49405
+ permissions,
49406
+ expiresInDays: this.expiresInDays && this.expiresInDays > 0 ? this.expiresInDays : undefined,
49407
+ })
49408
+ .subscribe({
49409
+ next: res => {
49410
+ this.submitting.set(false);
49411
+ this.showCreate.set(false);
49412
+ this.secret.set(res.key);
49413
+ },
49414
+ error: err => {
49415
+ this.submitting.set(false);
49416
+ this.errorMsg.set(err?.error?.message ?? this.t('genericError'));
49417
+ },
49418
+ });
49419
+ }
49420
+ revoke(key) {
49421
+ if (!confirm(this.t('revokeConfirm')))
49422
+ return;
49423
+ this.apiKeys.revoke(key.keyId).subscribe({
49424
+ next: () => this.refresh(),
49425
+ error: err => this.errorMsg.set(err?.error?.message ?? this.t('genericError')),
49426
+ });
49427
+ }
49428
+ async copy(secret) {
49429
+ try {
49430
+ await navigator.clipboard.writeText(secret);
49431
+ this.copied.set(true);
49432
+ }
49433
+ catch {
49434
+ // clipboard no disponible — el user puede seleccionar el texto manualmente
49435
+ }
49436
+ }
49437
+ finishSecret() {
49438
+ this.secret.set(null);
49439
+ this.copied.set(false);
49440
+ this.refresh();
49441
+ }
49442
+ dismiss() {
49443
+ this._modalRef?.dismiss(null, 'cancel');
49444
+ }
49445
+ t(key) {
49446
+ return this.i18n.t(key, this.i18nNamespace);
49447
+ }
49448
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ApiKeysModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
49449
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: ApiKeysModalComponent, isStandalone: true, selector: "val-api-keys-modal", inputs: { _modalRef: "_modalRef", i18nNamespace: "i18nNamespace" }, ngImport: i0, template: `
49450
+ <ion-header>
49451
+ <ion-toolbar>
49452
+ <ion-buttons slot="end">
49453
+ <ion-button fill="clear" color="dark" (click)="dismiss()">
49454
+ <strong>{{ t('close') }}</strong>
49455
+ </ion-button>
49456
+ </ion-buttons>
49457
+ </ion-toolbar>
49458
+ </ion-header>
49459
+
49460
+ <ion-content class="ion-padding">
49461
+ <val-display [props]="{ content: t('title'), size: 'small', color: 'dark' }" />
49462
+ <val-title [props]="{ content: t('hint'), size: 'large', color: '', bold: false }" />
49463
+
49464
+ @if (secret(); as s) {
49465
+ <!-- Secreto recién creado: mostrar UNA vez -->
49466
+ <div class="apikey-secret">
49467
+ <val-title [props]="{ content: t('secretTitle'), size: 'large', color: '', bold: true }" />
49468
+ <p class="apikey-secret__warn">{{ t('secretWarning') }}</p>
49469
+ <div class="apikey-secret__box">
49470
+ <code>{{ s.secret }}</code>
49471
+ </div>
49472
+ <div class="apikey-actions">
49473
+ <ion-button expand="block" color="dark" (click)="copy(s.secret)">
49474
+ {{ copied() ? t('copied') : t('copy') }}
49475
+ </ion-button>
49476
+ <ion-button expand="block" fill="outline" color="dark" (click)="finishSecret()">
49477
+ {{ t('done') }}
49478
+ </ion-button>
49479
+ </div>
49480
+ </div>
49481
+ } @else {
49482
+ @if (loading()) {
49483
+ <ion-spinner name="dots" />
49484
+ } @else {
49485
+ <!-- Lista -->
49486
+ @if (keys().length === 0) {
49487
+ <val-text [props]="{ content: t('empty'), size: 'medium', color: 'medium', bold: false }" />
49488
+ } @else {
49489
+ <div class="apikey-list">
49490
+ @for (k of keys(); track k.keyId) {
49491
+ <div class="apikey-row">
49492
+ <div class="apikey-row__main">
49493
+ <span class="apikey-row__name">{{ k.name }}</span>
49494
+ <span class="apikey-row__meta">
49495
+ {{ k.permissions.length }} {{ t('permsCount') }} · {{ t('lastUsed') }}:
49496
+ {{ k.lastUsedAt || t('never') }}
49497
+ </span>
49498
+ </div>
49499
+ <ion-button fill="clear" color="danger" size="small" (click)="revoke(k)">
49500
+ {{ t('revoke') }}
49501
+ </ion-button>
49502
+ </div>
49503
+ }
49504
+ </div>
49505
+ }
49506
+
49507
+ @if (errorMsg()) {
49508
+ <p class="apikey-error">{{ errorMsg() }}</p>
49509
+ }
49510
+
49511
+ <!-- Crear -->
49512
+ @if (showCreate()) {
49513
+ <div class="apikey-create">
49514
+ <ion-input
49515
+ label="{{ t('nameLabel') }}"
49516
+ labelPlacement="stacked"
49517
+ [placeholder]="t('namePlaceholder')"
49518
+ [(ngModel)]="name"
49519
+ />
49520
+ <p class="apikey-create__label">{{ t('permissionsLabel') }}</p>
49521
+ <div class="apikey-perms">
49522
+ @for (p of availablePermissions(); track p) {
49523
+ <ion-checkbox
49524
+ labelPlacement="end"
49525
+ [checked]="selectedPerms().has(p)"
49526
+ (ionChange)="togglePerm(p, $event)"
49527
+ >
49528
+ {{ p }}
49529
+ </ion-checkbox>
49530
+ }
49531
+ </div>
49532
+ <ion-input
49533
+ label="{{ t('expiresLabel') }}"
49534
+ labelPlacement="stacked"
49535
+ type="number"
49536
+ [(ngModel)]="expiresInDays"
49537
+ />
49538
+ <div class="apikey-actions">
49539
+ <ion-button expand="block" color="dark" [disabled]="submitting()" (click)="submit()">
49540
+ @if (submitting()) {
49541
+ <ion-spinner name="dots" />&nbsp;{{ t('creating') }}
49542
+ } @else {
49543
+ {{ t('create') }}
49544
+ }
49545
+ </ion-button>
49546
+ <ion-button expand="block" fill="clear" color="medium" (click)="showCreate.set(false)">
49547
+ {{ t('cancel') }}
49548
+ </ion-button>
49549
+ </div>
49550
+ </div>
49551
+ } @else {
49552
+ <div class="apikey-actions">
49553
+ <ion-button expand="block" color="primary" (click)="openCreate()">
49554
+ {{ t('newKey') }}
49555
+ </ion-button>
49556
+ </div>
49557
+ }
49558
+ }
49559
+ }
49560
+ </ion-content>
49561
+ `, isInline: true, styles: ["val-display{display:block;margin-bottom:4px}val-title{display:block;margin-bottom:12px}.apikey-list{display:flex;flex-direction:column;gap:8px;margin-bottom:16px}.apikey-row{display:flex;align-items:center;justify-content:space-between;padding:12px;border-radius:10px;border:1.5px solid var(--ion-color-light)}.apikey-row__main{display:flex;flex-direction:column}.apikey-row__name{font-weight:600;color:var(--ion-color-dark)}.apikey-row__meta{font-size:12px;color:var(--ion-color-medium-shade, #6b6e76)}.apikey-create{margin-top:8px;padding:16px;border-radius:12px;background:var(--ion-color-light)}.apikey-create__label{margin:12px 0 4px;font-size:13px;font-weight:600;color:var(--ion-color-dark)}.apikey-perms{display:flex;flex-direction:column;gap:6px;margin-bottom:12px;font-size:13px}.apikey-actions{margin-top:16px;display:flex;flex-direction:column;gap:8px}.apikey-error{margin:8px 0;font-size:.875rem;color:var(--ion-color-danger, #c0392b)}.apikey-secret__warn{font-size:.875rem;color:var(--ion-color-warning-shade, #b88600);margin:0 0 12px}.apikey-secret__box{padding:12px;border-radius:8px;background:var(--ion-color-dark);color:#fff;word-break:break-all;font-family:monospace;font-size:13px}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$7.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$7.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { 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: IonInput, selector: "ion-input", inputs: ["accept", "autocapitalize", "autocomplete", "autocorrect", "autofocus", "clearInput", "clearOnEdit", "color", "counter", "counterFormatter", "debounce", "disabled", "enterkeyhint", "errorText", "fill", "helperText", "inputmode", "label", "labelPlacement", "max", "maxlength", "min", "minlength", "mode", "multiple", "name", "pattern", "placeholder", "readonly", "required", "shape", "size", "spellcheck", "step", "type", "value"] }, { kind: "component", type: IonCheckbox, selector: "ion-checkbox", inputs: ["checked", "color", "disabled", "errorText", "helperText", "indeterminate", "justify", "labelPlacement", "mode", "name", "value"] }, { kind: "component", type: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { kind: "component", type: DisplayComponent, selector: "val-display", inputs: ["props"] }, { kind: "component", type: TextComponent, selector: "val-text", inputs: ["props"] }, { kind: "component", type: TitleComponent, selector: "val-title", inputs: ["props"] }] }); }
49562
+ }
49563
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ApiKeysModalComponent, decorators: [{
49564
+ type: Component,
49565
+ args: [{ selector: 'val-api-keys-modal', standalone: true, imports: [
49566
+ FormsModule,
49567
+ IonHeader,
49568
+ IonToolbar,
49569
+ IonButtons,
49570
+ IonButton,
49571
+ IonContent,
49572
+ IonInput,
49573
+ IonCheckbox,
49574
+ IonSpinner,
49575
+ DisplayComponent,
49576
+ TextComponent,
49577
+ TitleComponent,
49578
+ ], template: `
49579
+ <ion-header>
49580
+ <ion-toolbar>
49581
+ <ion-buttons slot="end">
49582
+ <ion-button fill="clear" color="dark" (click)="dismiss()">
49583
+ <strong>{{ t('close') }}</strong>
49584
+ </ion-button>
49585
+ </ion-buttons>
49586
+ </ion-toolbar>
49587
+ </ion-header>
49588
+
49589
+ <ion-content class="ion-padding">
49590
+ <val-display [props]="{ content: t('title'), size: 'small', color: 'dark' }" />
49591
+ <val-title [props]="{ content: t('hint'), size: 'large', color: '', bold: false }" />
49592
+
49593
+ @if (secret(); as s) {
49594
+ <!-- Secreto recién creado: mostrar UNA vez -->
49595
+ <div class="apikey-secret">
49596
+ <val-title [props]="{ content: t('secretTitle'), size: 'large', color: '', bold: true }" />
49597
+ <p class="apikey-secret__warn">{{ t('secretWarning') }}</p>
49598
+ <div class="apikey-secret__box">
49599
+ <code>{{ s.secret }}</code>
49600
+ </div>
49601
+ <div class="apikey-actions">
49602
+ <ion-button expand="block" color="dark" (click)="copy(s.secret)">
49603
+ {{ copied() ? t('copied') : t('copy') }}
49604
+ </ion-button>
49605
+ <ion-button expand="block" fill="outline" color="dark" (click)="finishSecret()">
49606
+ {{ t('done') }}
49607
+ </ion-button>
49608
+ </div>
49609
+ </div>
49610
+ } @else {
49611
+ @if (loading()) {
49612
+ <ion-spinner name="dots" />
49613
+ } @else {
49614
+ <!-- Lista -->
49615
+ @if (keys().length === 0) {
49616
+ <val-text [props]="{ content: t('empty'), size: 'medium', color: 'medium', bold: false }" />
49617
+ } @else {
49618
+ <div class="apikey-list">
49619
+ @for (k of keys(); track k.keyId) {
49620
+ <div class="apikey-row">
49621
+ <div class="apikey-row__main">
49622
+ <span class="apikey-row__name">{{ k.name }}</span>
49623
+ <span class="apikey-row__meta">
49624
+ {{ k.permissions.length }} {{ t('permsCount') }} · {{ t('lastUsed') }}:
49625
+ {{ k.lastUsedAt || t('never') }}
49626
+ </span>
49627
+ </div>
49628
+ <ion-button fill="clear" color="danger" size="small" (click)="revoke(k)">
49629
+ {{ t('revoke') }}
49630
+ </ion-button>
49631
+ </div>
49632
+ }
49633
+ </div>
49634
+ }
49635
+
49636
+ @if (errorMsg()) {
49637
+ <p class="apikey-error">{{ errorMsg() }}</p>
49638
+ }
49639
+
49640
+ <!-- Crear -->
49641
+ @if (showCreate()) {
49642
+ <div class="apikey-create">
49643
+ <ion-input
49644
+ label="{{ t('nameLabel') }}"
49645
+ labelPlacement="stacked"
49646
+ [placeholder]="t('namePlaceholder')"
49647
+ [(ngModel)]="name"
49648
+ />
49649
+ <p class="apikey-create__label">{{ t('permissionsLabel') }}</p>
49650
+ <div class="apikey-perms">
49651
+ @for (p of availablePermissions(); track p) {
49652
+ <ion-checkbox
49653
+ labelPlacement="end"
49654
+ [checked]="selectedPerms().has(p)"
49655
+ (ionChange)="togglePerm(p, $event)"
49656
+ >
49657
+ {{ p }}
49658
+ </ion-checkbox>
49659
+ }
49660
+ </div>
49661
+ <ion-input
49662
+ label="{{ t('expiresLabel') }}"
49663
+ labelPlacement="stacked"
49664
+ type="number"
49665
+ [(ngModel)]="expiresInDays"
49666
+ />
49667
+ <div class="apikey-actions">
49668
+ <ion-button expand="block" color="dark" [disabled]="submitting()" (click)="submit()">
49669
+ @if (submitting()) {
49670
+ <ion-spinner name="dots" />&nbsp;{{ t('creating') }}
49671
+ } @else {
49672
+ {{ t('create') }}
49673
+ }
49674
+ </ion-button>
49675
+ <ion-button expand="block" fill="clear" color="medium" (click)="showCreate.set(false)">
49676
+ {{ t('cancel') }}
49677
+ </ion-button>
49678
+ </div>
49679
+ </div>
49680
+ } @else {
49681
+ <div class="apikey-actions">
49682
+ <ion-button expand="block" color="primary" (click)="openCreate()">
49683
+ {{ t('newKey') }}
49684
+ </ion-button>
49685
+ </div>
49686
+ }
49687
+ }
49688
+ }
49689
+ </ion-content>
49690
+ `, styles: ["val-display{display:block;margin-bottom:4px}val-title{display:block;margin-bottom:12px}.apikey-list{display:flex;flex-direction:column;gap:8px;margin-bottom:16px}.apikey-row{display:flex;align-items:center;justify-content:space-between;padding:12px;border-radius:10px;border:1.5px solid var(--ion-color-light)}.apikey-row__main{display:flex;flex-direction:column}.apikey-row__name{font-weight:600;color:var(--ion-color-dark)}.apikey-row__meta{font-size:12px;color:var(--ion-color-medium-shade, #6b6e76)}.apikey-create{margin-top:8px;padding:16px;border-radius:12px;background:var(--ion-color-light)}.apikey-create__label{margin:12px 0 4px;font-size:13px;font-weight:600;color:var(--ion-color-dark)}.apikey-perms{display:flex;flex-direction:column;gap:6px;margin-bottom:12px;font-size:13px}.apikey-actions{margin-top:16px;display:flex;flex-direction:column;gap:8px}.apikey-error{margin:8px 0;font-size:.875rem;color:var(--ion-color-danger, #c0392b)}.apikey-secret__warn{font-size:.875rem;color:var(--ion-color-warning-shade, #b88600);margin:0 0 12px}.apikey-secret__box{padding:12px;border-radius:8px;background:var(--ion-color-dark);color:#fff;word-break:break-all;font-family:monospace;font-size:13px}\n"] }]
49691
+ }], ctorParameters: () => [], propDecorators: { _modalRef: [{
49692
+ type: Input
49693
+ }], i18nNamespace: [{
49694
+ type: Input
49695
+ }] } });
49696
+
48834
49697
  /**
48835
49698
  * Defaults i18n (es/en) embebidos en `val-organization-view`. Auto-registrados en
48836
49699
  * el constructor del componente si el consumer no proveyó el namespace
@@ -48886,6 +49749,8 @@ const ORGANIZATION_VIEW_I18N = {
48886
49749
  inviteCtaTitle: '¿Deseas agregar a alguien?',
48887
49750
  inviteCtaHint: 'Invita a usuarios a unirse a tu organización.',
48888
49751
  inviteOpen: 'Invitar',
49752
+ importOpen: 'Importar',
49753
+ apiKeysOpen: 'API Keys',
48889
49754
  viewMemberCta: 'Ver miembro',
48890
49755
  roleViewer: 'Visor',
48891
49756
  roleEditor: 'Editor',
@@ -48955,6 +49820,8 @@ const ORGANIZATION_VIEW_I18N = {
48955
49820
  inviteCtaTitle: 'Want to add someone?',
48956
49821
  inviteCtaHint: 'Invite users to join your organization.',
48957
49822
  inviteOpen: 'Invite',
49823
+ importOpen: 'Import',
49824
+ apiKeysOpen: 'API Keys',
48958
49825
  viewMemberCta: 'View member',
48959
49826
  roleViewer: 'Viewer',
48960
49827
  roleEditor: 'Editor',
@@ -49068,6 +49935,22 @@ class OrganizationViewComponent {
49068
49935
  this.members = signal([]);
49069
49936
  this.membersLoading = signal(false);
49070
49937
  this.membersLoadError = signal(null);
49938
+ this.membersErrorState = computed(() => {
49939
+ this.i18n.lang();
49940
+ const err = this.membersLoadError();
49941
+ if (!err)
49942
+ return null;
49943
+ return createErrorStateProps(err, {
49944
+ title: { offline: this.tt('offlineTitle'), error: this.tt('errorTitle') },
49945
+ description: {
49946
+ offline: this.tt('offlineHint'),
49947
+ error: interpretError(err).message,
49948
+ },
49949
+ retryLabel: this.tt('retry'),
49950
+ onRetry: () => this.loadMembers(),
49951
+ retrying: this.membersLoading(),
49952
+ });
49953
+ });
49071
49954
  this.membersNextToken = signal(null);
49072
49955
  this.loadingMoreMembers = signal(false);
49073
49956
  this.showAllMembers = signal(false);
@@ -49154,6 +50037,22 @@ class OrganizationViewComponent {
49154
50037
  shape: 'round',
49155
50038
  type: 'button',
49156
50039
  }));
50040
+ this.openImportButtonProps = computed(() => ({
50041
+ text: this.tt('importOpen'),
50042
+ color: 'dark',
50043
+ fill: 'outline',
50044
+ size: 'default',
50045
+ shape: 'round',
50046
+ type: 'button',
50047
+ }));
50048
+ this.openApiKeysButtonProps = computed(() => ({
50049
+ text: this.tt('apiKeysOpen'),
50050
+ color: 'dark',
50051
+ fill: 'clear',
50052
+ size: 'default',
50053
+ shape: 'round',
50054
+ type: 'button',
50055
+ }));
49157
50056
  /** Guarda para detectar el primer disparo del effect (evita doble carga). */
49158
50057
  this.lastLoadedOrgId = null;
49159
50058
  const ns = this.ns;
@@ -49254,6 +50153,36 @@ class OrganizationViewComponent {
49254
50153
  backdropDismiss: true,
49255
50154
  });
49256
50155
  }
50156
+ onOpenImport() {
50157
+ void this.modalService.open({
50158
+ component: MemberImportModalComponent,
50159
+ componentProps: {
50160
+ orgId: this.activeOrgId(),
50161
+ availableRoles: this.availableRoles().map(r => r.name),
50162
+ onSuccess: () => {
50163
+ this.loadMembers();
50164
+ this.resolvedConfig().onMemberInvited?.();
50165
+ },
50166
+ },
50167
+ breakpoints: {
50168
+ breakpoints: [0, 0.92],
50169
+ initialBreakpoint: 0.92,
50170
+ showHandle: true,
50171
+ },
50172
+ backdropDismiss: true,
50173
+ });
50174
+ }
50175
+ onOpenApiKeys() {
50176
+ void this.modalService.open({
50177
+ component: ApiKeysModalComponent,
50178
+ breakpoints: {
50179
+ breakpoints: [0, 0.92],
50180
+ initialBreakpoint: 0.92,
50181
+ showHandle: true,
50182
+ },
50183
+ backdropDismiss: true,
50184
+ });
50185
+ }
49257
50186
  onViewPermissions() {
49258
50187
  this.nav.navigateByUrl(this.resolvedConfig().viewPermissionsRoute);
49259
50188
  }
@@ -49335,6 +50264,7 @@ class OrganizationViewComponent {
49335
50264
  if (!orgId)
49336
50265
  return Promise.resolve();
49337
50266
  this.membersLoading.set(true);
50267
+ this.membersLoadError.set(null);
49338
50268
  this.membersNextToken.set(null);
49339
50269
  this.showAllMembers.set(false);
49340
50270
  return new Promise(resolve => {
@@ -49545,6 +50475,8 @@ class OrganizationViewComponent {
49545
50475
  <div class="section-body">
49546
50476
  @if (membersLoading()) {
49547
50477
  <val-skeleton-layout [props]="{ preset: 'list', rows: 3 }" />
50478
+ } @else if (membersErrorState()) {
50479
+ <val-empty-state [props]="membersErrorState()!" />
49548
50480
  } @else if (members().length === 0) {
49549
50481
  <val-text
49550
50482
  [props]="{
@@ -49637,6 +50569,8 @@ class OrganizationViewComponent {
49637
50569
  />
49638
50570
  </div>
49639
50571
  <val-button [props]="openInviteButtonProps()" (click)="onOpenInvite()" />
50572
+ <val-button [props]="openImportButtonProps()" (click)="onOpenImport()" />
50573
+ <val-button [props]="openApiKeysButtonProps()" (click)="onOpenApiKeys()" />
49640
50574
  </div>
49641
50575
  </section>
49642
50576
  }
@@ -49745,6 +50679,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
49745
50679
  ButtonComponent,
49746
50680
  MemberCardComponent,
49747
50681
  TransferOwnershipModalComponent,
50682
+ MemberImportModalComponent,
50683
+ ApiKeysModalComponent,
49748
50684
  ], template: `
49749
50685
  <div class="page">
49750
50686
  <header class="page-header">
@@ -49823,6 +50759,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
49823
50759
  <div class="section-body">
49824
50760
  @if (membersLoading()) {
49825
50761
  <val-skeleton-layout [props]="{ preset: 'list', rows: 3 }" />
50762
+ } @else if (membersErrorState()) {
50763
+ <val-empty-state [props]="membersErrorState()!" />
49826
50764
  } @else if (members().length === 0) {
49827
50765
  <val-text
49828
50766
  [props]="{
@@ -49915,6 +50853,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
49915
50853
  />
49916
50854
  </div>
49917
50855
  <val-button [props]="openInviteButtonProps()" (click)="onOpenInvite()" />
50856
+ <val-button [props]="openImportButtonProps()" (click)="onOpenImport()" />
50857
+ <val-button [props]="openApiKeysButtonProps()" (click)="onOpenApiKeys()" />
49918
50858
  </div>
49919
50859
  </section>
49920
50860
  }
@@ -62662,5 +63602,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
62662
63602
  * Generated bundle index. Do not edit.
62663
63603
  */
62664
63604
 
62665
- 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, NotificationPreferencesViewComponent, NotificationsService, NotificationsViewComponent, 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, PermissionCatalogService, PermissionsViewComponent, 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, provideValtechNotificationPreferencesRoutes, provideValtechNotificationsRoutes, provideValtechOrganizationRoutes, provideValtechPermissionsRoutes, provideValtechPreferencesRoutes, provideValtechPresets, provideValtechProfileRoutes, provideValtechSecurityRoutes, provideValtechSettingsRoutes, provideValtechSite, provideValtechSkeleton, query, renderPatternSvgInner, replaceSpecialChars, resolveColor$1 as resolveColor, resolveInputDefaultValue, resolveWebBaseUrl, roleGuard, storagePaths, superAdminGuard, toArticle };
63605
+ 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, ApiKeyService, ApiKeysModalComponent, 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, MemberImportModalComponent, MenuComponent, MessagingService, MetaService, MetricCardComponent, MfaModalComponent, ModalService, ModalShellComponent, MultiSelectSearchComponent, NUM_KEYS, NavigationService, NetworkBannerComponent, NetworkStatusService, NewsBuilder, NoContentComponent, NotesBoxComponent, NotificationActionService, NotificationPreferencesViewComponent, NotificationsService, NotificationsViewComponent, 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, PermissionCatalogService, PermissionsViewComponent, 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, provideValtechNotificationPreferencesRoutes, provideValtechNotificationsRoutes, provideValtechOrganizationRoutes, provideValtechPermissionsRoutes, provideValtechPreferencesRoutes, provideValtechPresets, provideValtechProfileRoutes, provideValtechSecurityRoutes, provideValtechSettingsRoutes, provideValtechSite, provideValtechSkeleton, query, renderPatternSvgInner, replaceSpecialChars, resolveColor$1 as resolveColor, resolveInputDefaultValue, resolveWebBaseUrl, roleGuard, storagePaths, superAdminGuard, toArticle };
62666
63606
  //# sourceMappingURL=valtech-components.mjs.map