valtech-components 2.0.998 → 2.0.999

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 (34) hide show
  1. package/esm2022/lib/components/organisms/organization-view/organization-view.component.mjs +1 -76
  2. package/esm2022/lib/components/organisms/organization-view/organization-view.i18n.mjs +1 -5
  3. package/esm2022/lib/components/organisms/permissions-view/permissions-view.component.mjs +14 -9
  4. package/esm2022/lib/components/organisms/permissions-view/permissions-view.i18n.mjs +3 -1
  5. package/esm2022/lib/services/auth/auth.service.mjs +1 -14
  6. package/esm2022/lib/services/auth/types.mjs +1 -1
  7. package/esm2022/lib/services/org/org.service.mjs +1 -8
  8. package/esm2022/lib/services/org/permission-catalog.service.mjs +1 -1
  9. package/esm2022/lib/services/org/types.mjs +1 -1
  10. package/esm2022/lib/version.mjs +2 -2
  11. package/esm2022/public-api.mjs +1 -4
  12. package/fesm2022/valtech-components.mjs +57 -990
  13. package/fesm2022/valtech-components.mjs.map +1 -1
  14. package/lib/components/organisms/organization-view/organization-view.component.d.ts +0 -5
  15. package/lib/services/auth/auth.service.d.ts +1 -7
  16. package/lib/services/auth/types.d.ts +0 -23
  17. package/lib/services/org/org.service.d.ts +1 -6
  18. package/lib/services/org/permission-catalog.service.d.ts +1 -1
  19. package/lib/services/org/types.d.ts +0 -30
  20. package/lib/version.d.ts +1 -1
  21. package/package.json +1 -1
  22. package/public-api.d.ts +1 -5
  23. package/esm2022/lib/components/organisms/api-keys-modal/api-keys-modal.component.mjs +0 -385
  24. package/esm2022/lib/components/organisms/api-keys-modal/api-keys-modal.i18n.mjs +0 -63
  25. package/esm2022/lib/components/organisms/member-import-modal/member-import-modal.component.mjs +0 -313
  26. package/esm2022/lib/components/organisms/member-import-modal/member-import-modal.i18n.mjs +0 -63
  27. package/esm2022/lib/services/apikeys/api-keys.service.mjs +0 -46
  28. package/esm2022/lib/services/apikeys/types.mjs +0 -7
  29. package/lib/components/organisms/api-keys-modal/api-keys-modal.component.d.ts +0 -44
  30. package/lib/components/organisms/api-keys-modal/api-keys-modal.i18n.d.ts +0 -6
  31. package/lib/components/organisms/member-import-modal/member-import-modal.component.d.ts +0 -47
  32. package/lib/components/organisms/member-import-modal/member-import-modal.i18n.d.ts +0 -6
  33. package/lib/services/apikeys/api-keys.service.d.ts +0 -25
  34. package/lib/services/apikeys/types.d.ts +0 -46
@@ -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.998';
57
+ const VERSION = '2.0.999';
58
58
 
59
59
  // Control de estado de refresco (singleton a nivel de módulo)
60
60
  let isRefreshing = false;
@@ -8658,19 +8658,6 @@ 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
- }
8674
8661
  /**
8675
8662
  * Cambia la contraseña del usuario autenticado.
8676
8663
  * Requiere la contraseña actual para verificación.
@@ -41750,7 +41737,7 @@ const PROFILE_VIEW_I18N = {
41750
41737
  const HANDLE_PATTERN = /^[a-z_][a-z0-9_]*$/;
41751
41738
  const HANDLE_MIN = 3;
41752
41739
  const HANDLE_MAX = 20;
41753
- const DEFAULT_NAMESPACE$h = 'Settings.Profile';
41740
+ const DEFAULT_NAMESPACE$f = 'Settings.Profile';
41754
41741
  /**
41755
41742
  * `val-profile-view` — vista Perfil full-feature autocontenida (organism).
41756
41743
  *
@@ -41800,7 +41787,7 @@ class ProfileViewComponent {
41800
41787
  showPhone: merged.showPhone ?? true,
41801
41788
  showShareCta: merged.showShareCta ?? true,
41802
41789
  profileBaseUrl: merged.profileBaseUrl ?? '',
41803
- i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$h,
41790
+ i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$f,
41804
41791
  onSaved: merged.onSaved,
41805
41792
  onAvatarUploaded: merged.onAvatarUploaded,
41806
41793
  };
@@ -42716,7 +42703,7 @@ const PREFERENCES_VIEW_I18N = {
42716
42703
  },
42717
42704
  };
42718
42705
 
42719
- const DEFAULT_NAMESPACE$g = 'Settings.General';
42706
+ const DEFAULT_NAMESPACE$e = 'Settings.General';
42720
42707
  const DEFAULT_LANGUAGES = ['es', 'en'];
42721
42708
  /**
42722
42709
  * `val-preferences-view` — vista de preferencias de UI cross-app (tema +
@@ -42753,7 +42740,7 @@ class PreferencesViewComponent {
42753
42740
  showLanguage: merged.showLanguage ?? true,
42754
42741
  showFontSize: merged.showFontSize ?? true,
42755
42742
  supportedLanguages: merged.supportedLanguages ?? DEFAULT_LANGUAGES,
42756
- i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$g,
42743
+ i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$e,
42757
42744
  onThemeChanged: merged.onThemeChanged,
42758
42745
  onLanguageChanged: merged.onLanguageChanged,
42759
42746
  onFontSizeChanged: merged.onFontSizeChanged,
@@ -44509,7 +44496,7 @@ const SECURITY_VIEW_I18N = {
44509
44496
  };
44510
44497
 
44511
44498
  addIcons({ laptopOutline, phonePortraitOutline });
44512
- const DEFAULT_NAMESPACE$f = 'Settings.Security';
44499
+ const DEFAULT_NAMESPACE$d = 'Settings.Security';
44513
44500
  /**
44514
44501
  * `val-security-view` — vista Seguridad full-feature autocontenida (organism).
44515
44502
  *
@@ -44558,7 +44545,7 @@ class SecurityViewComponent {
44558
44545
  showChangeEmail: merged.showChangeEmail ?? true,
44559
44546
  showMfa: merged.showMfa ?? true,
44560
44547
  showPolicies: merged.showPolicies ?? true,
44561
- i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$f,
44548
+ i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$d,
44562
44549
  handleDeepLinks: merged.handleDeepLinks ?? true,
44563
44550
  homeRoute: merged.homeRoute ?? '/app/home',
44564
44551
  onMfaCompleted: merged.onMfaCompleted,
@@ -45360,13 +45347,6 @@ class OrgService {
45360
45347
  inviteUser(orgId, req) {
45361
45348
  return this.http.post(`${this.baseUrl}/${orgId}/invite`, req);
45362
45349
  }
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
- }
45370
45350
  leaveOrg(orgId) {
45371
45351
  return this.http.post(`${this.baseUrl}/${orgId}/leave`, {});
45372
45352
  }
@@ -45471,7 +45451,7 @@ const CREATE_ORG_MODAL_I18N = {
45471
45451
  },
45472
45452
  };
45473
45453
 
45474
- const DEFAULT_NAMESPACE$e = 'CreateOrgModal';
45454
+ const DEFAULT_NAMESPACE$c = 'CreateOrgModal';
45475
45455
  /**
45476
45456
  * `val-create-org-modal` — modal de creación de organización (organism
45477
45457
  * compartido). Promovido desde `showcase` bajo el proceso de ADR-021. Reusado por
@@ -45497,10 +45477,10 @@ class CreateOrgModalComponent {
45497
45477
  this.toast = inject(ToastService);
45498
45478
  this.errors = inject(ValtechErrorService);
45499
45479
  /** Namespace i18n con que la vista resuelve sus textos. */
45500
- this.i18nNamespace = DEFAULT_NAMESPACE$e;
45480
+ this.i18nNamespace = DEFAULT_NAMESPACE$c;
45501
45481
  this._busy = signal(false);
45502
- if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$e)) {
45503
- this.i18n.registerContent(DEFAULT_NAMESPACE$e, CREATE_ORG_MODAL_I18N);
45482
+ if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$c)) {
45483
+ this.i18n.registerContent(DEFAULT_NAMESPACE$c, CREATE_ORG_MODAL_I18N);
45504
45484
  }
45505
45485
  this.formMeta = signal(this.buildFormMeta());
45506
45486
  effect(() => {
@@ -45736,7 +45716,7 @@ const DELETE_ACCOUNT_MODAL_I18N = {
45736
45716
  },
45737
45717
  };
45738
45718
 
45739
- const DEFAULT_NAMESPACE$d = 'Settings.DeleteAccount';
45719
+ const DEFAULT_NAMESPACE$b = 'Settings.DeleteAccount';
45740
45720
  /**
45741
45721
  * `val-delete-account-modal` — modal de eliminación de cuenta (organism,
45742
45722
  * cuenta-específico). Promovido desde `showcase` bajo el proceso de ADR-021.
@@ -45762,7 +45742,7 @@ class DeleteAccountModalComponent {
45762
45742
  this.i18n = inject(I18nService);
45763
45743
  this.toast = inject(ToastService);
45764
45744
  /** Namespace i18n con que la vista resuelve sus textos. */
45765
- this.i18nNamespace = DEFAULT_NAMESPACE$d;
45745
+ this.i18nNamespace = DEFAULT_NAMESPACE$b;
45766
45746
  this.hasPassword = signal(null);
45767
45747
  this.codeSent = signal(false);
45768
45748
  this.formState = signal(ComponentStates.ENABLED);
@@ -45829,8 +45809,8 @@ class DeleteAccountModalComponent {
45829
45809
  ],
45830
45810
  actions: this.submitBtn('delete-code-submit', 'confirmCta'),
45831
45811
  }));
45832
- if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$d)) {
45833
- this.i18n.registerContent(DEFAULT_NAMESPACE$d, DELETE_ACCOUNT_MODAL_I18N);
45812
+ if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$b)) {
45813
+ this.i18n.registerContent(DEFAULT_NAMESPACE$b, DELETE_ACCOUNT_MODAL_I18N);
45834
45814
  }
45835
45815
  }
45836
45816
  ngOnInit() {
@@ -46031,7 +46011,7 @@ const ORG_INFO_SHEET_I18N = {
46031
46011
  },
46032
46012
  };
46033
46013
 
46034
- const DEFAULT_NAMESPACE$c = 'OrgInfoSheet';
46014
+ const DEFAULT_NAMESPACE$a = 'OrgInfoSheet';
46035
46015
  /**
46036
46016
  * `val-org-info-sheet` — sheet informativo "¿Qué son las organizaciones?".
46037
46017
  * Extraído del `OrgInfoSheetComponent` inline de `AccountPage` bajo el proceso de
@@ -46076,15 +46056,15 @@ class OrgInfoSheetComponent {
46076
46056
  text: this.closeProps.text || this.resolvedCloseLabel(),
46077
46057
  }));
46078
46058
  addIcons({ peopleOutline, swapHorizontalOutline, mailOutline });
46079
- if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$c)) {
46080
- this.i18n.registerContent(DEFAULT_NAMESPACE$c, ORG_INFO_SHEET_I18N);
46059
+ if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$a)) {
46060
+ this.i18n.registerContent(DEFAULT_NAMESPACE$a, ORG_INFO_SHEET_I18N);
46081
46061
  }
46082
46062
  }
46083
46063
  dismiss() {
46084
46064
  this._modalRef?.dismiss(null, 'cancel');
46085
46065
  }
46086
46066
  tt(key) {
46087
- return this.i18n.t(key, DEFAULT_NAMESPACE$c);
46067
+ return this.i18n.t(key, DEFAULT_NAMESPACE$a);
46088
46068
  }
46089
46069
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: OrgInfoSheetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
46090
46070
  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: `
@@ -46251,7 +46231,7 @@ const SWITCH_ORG_MODAL_I18N = {
46251
46231
  },
46252
46232
  };
46253
46233
 
46254
- const DEFAULT_NAMESPACE$b = 'Settings.SwitchOrg';
46234
+ const DEFAULT_NAMESPACE$9 = 'Settings.SwitchOrg';
46255
46235
  addIcons({ businessOutline, checkmarkCircleOutline });
46256
46236
  /**
46257
46237
  * `val-switch-org-modal` — modal de cambio de organización activa (organism
@@ -46277,7 +46257,7 @@ class SwitchOrgModalComponent {
46277
46257
  this.orgSwitch = inject(OrgSwitchService);
46278
46258
  this.errors = inject(ValtechErrorService);
46279
46259
  /** Namespace i18n con que la vista resuelve sus textos. */
46280
- this.i18nNamespace = DEFAULT_NAMESPACE$b;
46260
+ this.i18nNamespace = DEFAULT_NAMESPACE$9;
46281
46261
  this.orgs = signal([]);
46282
46262
  this.loading = signal(false);
46283
46263
  this.query = signal('');
@@ -46293,8 +46273,8 @@ class SwitchOrgModalComponent {
46293
46273
  return list;
46294
46274
  return list.filter(o => o.name.toLowerCase().includes(q));
46295
46275
  });
46296
- if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$b)) {
46297
- this.i18n.registerContent(DEFAULT_NAMESPACE$b, SWITCH_ORG_MODAL_I18N);
46276
+ if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$9)) {
46277
+ this.i18n.registerContent(DEFAULT_NAMESPACE$9, SWITCH_ORG_MODAL_I18N);
46298
46278
  }
46299
46279
  this.loadOrgs();
46300
46280
  }
@@ -46612,7 +46592,7 @@ const ACCOUNT_VIEW_I18N = {
46612
46592
  };
46613
46593
 
46614
46594
  addIcons({ peopleOutline, businessOutline, chevronForwardOutline, swapHorizontalOutline, mailOutline });
46615
- const DEFAULT_NAMESPACE$a = 'Settings.Account';
46595
+ const DEFAULT_NAMESPACE$8 = 'Settings.Account';
46616
46596
  const DEFAULT_MANAGE_ORG_ROUTE = '/app/settings/organization';
46617
46597
  /**
46618
46598
  * `val-account-view` — vista Cuenta full-feature autocontenida (organism).
@@ -46653,7 +46633,7 @@ class AccountViewComponent {
46653
46633
  showNewOrgCta: merged.showNewOrgCta ?? true,
46654
46634
  showLogout: merged.showLogout ?? true,
46655
46635
  showDeleteAccount: merged.showDeleteAccount ?? true,
46656
- i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$a,
46636
+ i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$8,
46657
46637
  manageOrgRoute: merged.manageOrgRoute ?? DEFAULT_MANAGE_ORG_ROUTE,
46658
46638
  onOrgSwitched: merged.onOrgSwitched,
46659
46639
  onOrgCreated: merged.onOrgCreated,
@@ -47378,7 +47358,7 @@ const EDIT_ORG_MODAL_I18N = {
47378
47358
  },
47379
47359
  };
47380
47360
 
47381
- const DEFAULT_NAMESPACE$9 = 'EditOrgModal';
47361
+ const DEFAULT_NAMESPACE$7 = 'EditOrgModal';
47382
47362
  /**
47383
47363
  * `val-edit-org-modal` — modal de edición de organización (nombre + descripción).
47384
47364
  * Promovido desde `showcase` bajo el proceso de ADR-021. Reusado por la vista de
@@ -47405,10 +47385,10 @@ class EditOrgModalComponent {
47405
47385
  /** Organización a editar (pasada por `componentProps`). */
47406
47386
  this.org = null;
47407
47387
  /** Namespace i18n con que la vista resuelve sus textos. */
47408
- this.i18nNamespace = DEFAULT_NAMESPACE$9;
47388
+ this.i18nNamespace = DEFAULT_NAMESPACE$7;
47409
47389
  this.saving = signal(false);
47410
- if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$9)) {
47411
- this.i18n.registerContent(DEFAULT_NAMESPACE$9, EDIT_ORG_MODAL_I18N);
47390
+ if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$7)) {
47391
+ this.i18n.registerContent(DEFAULT_NAMESPACE$7, EDIT_ORG_MODAL_I18N);
47412
47392
  }
47413
47393
  this.formMeta = signal(this.buildFormMeta());
47414
47394
  effect(() => {
@@ -47646,7 +47626,7 @@ const INVITE_MEMBER_MODAL_I18N = {
47646
47626
  },
47647
47627
  };
47648
47628
 
47649
- const DEFAULT_NAMESPACE$8 = 'InviteModal';
47629
+ const DEFAULT_NAMESPACE$6 = 'InviteModal';
47650
47630
  addIcons({ personOutline, mailOutline, closeOutline, checkmarkCircleOutline });
47651
47631
  /**
47652
47632
  * `val-invite-member-modal` — invitación de miembros a una organización.
@@ -47677,7 +47657,7 @@ class InviteMemberModalComponent {
47677
47657
  /** Ids de miembros ya existentes (excluidos de los resultados). */
47678
47658
  this.existingMemberIds = [];
47679
47659
  /** Namespace i18n con que la vista resuelve sus textos. */
47680
- this.i18nNamespace = DEFAULT_NAMESPACE$8;
47660
+ this.i18nNamespace = DEFAULT_NAMESPACE$6;
47681
47661
  this.query = signal('');
47682
47662
  this.searching = signal(false);
47683
47663
  this.searchResults = signal([]);
@@ -47724,8 +47704,8 @@ class InviteMemberModalComponent {
47724
47704
  state: this.sending() ? ComponentStates.WORKING : ComponentStates.ENABLED,
47725
47705
  }));
47726
47706
  this.searchSubject = new Subject();
47727
- if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$8)) {
47728
- this.i18n.registerContent(DEFAULT_NAMESPACE$8, INVITE_MEMBER_MODAL_I18N);
47707
+ if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$6)) {
47708
+ this.i18n.registerContent(DEFAULT_NAMESPACE$6, INVITE_MEMBER_MODAL_I18N);
47729
47709
  }
47730
47710
  this.loadRoles();
47731
47711
  this.roleSub = this.roleControl.valueChanges.subscribe(v => {
@@ -48214,7 +48194,7 @@ const MEMBER_DETAIL_MODAL_I18N = {
48214
48194
  },
48215
48195
  };
48216
48196
 
48217
- const DEFAULT_NAMESPACE$7 = 'Settings.MemberDetail';
48197
+ const DEFAULT_NAMESPACE$5 = 'Settings.MemberDetail';
48218
48198
  /**
48219
48199
  * `val-member-detail-modal` — detalle de miembro de organización: perfil, rol y
48220
48200
  * permisos. Promovido desde `showcase` bajo el proceso de ADR-021.
@@ -48251,7 +48231,7 @@ class MemberDetailModalComponent {
48251
48231
  { id: 'admin', name: 'admin' },
48252
48232
  ];
48253
48233
  /** Namespace i18n con que la vista resuelve sus textos. */
48254
- this.i18nNamespace = DEFAULT_NAMESPACE$7;
48234
+ this.i18nNamespace = DEFAULT_NAMESPACE$5;
48255
48235
  this.working = signal(false);
48256
48236
  this.currentRole = signal('');
48257
48237
  this.roleControl = new FormControl('');
@@ -48291,8 +48271,8 @@ class MemberDetailModalComponent {
48291
48271
  type: 'button',
48292
48272
  state: this.working() ? ComponentStates.WORKING : ComponentStates.ENABLED,
48293
48273
  }));
48294
- if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$7)) {
48295
- this.i18n.registerContent(DEFAULT_NAMESPACE$7, MEMBER_DETAIL_MODAL_I18N);
48274
+ if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$5)) {
48275
+ this.i18n.registerContent(DEFAULT_NAMESPACE$5, MEMBER_DETAIL_MODAL_I18N);
48296
48276
  }
48297
48277
  }
48298
48278
  ngOnInit() {
@@ -48629,7 +48609,7 @@ const TRANSFER_OWNERSHIP_MODAL_I18N = {
48629
48609
  },
48630
48610
  };
48631
48611
 
48632
- const DEFAULT_NAMESPACE$6 = 'Settings.TransferModal';
48612
+ const DEFAULT_NAMESPACE$4 = 'Settings.TransferModal';
48633
48613
  /**
48634
48614
  * `val-transfer-ownership-modal` — selector de miembro para transferir la
48635
48615
  * propiedad de la organización. Promovido desde `showcase` bajo el proceso de
@@ -48653,7 +48633,7 @@ class TransferOwnershipModalComponent {
48653
48633
  /** Lista de miembros transferibles (todos excepto el owner actual). */
48654
48634
  this.members = [];
48655
48635
  /** Namespace i18n con que la vista resuelve sus textos. */
48656
- this.i18nNamespace = DEFAULT_NAMESPACE$6;
48636
+ this.i18nNamespace = DEFAULT_NAMESPACE$4;
48657
48637
  this.query = signal('');
48658
48638
  this.selectedId = signal('');
48659
48639
  this.filtered = computed(() => {
@@ -48665,8 +48645,8 @@ class TransferOwnershipModalComponent {
48665
48645
  m.userId.toLowerCase().includes(q));
48666
48646
  });
48667
48647
  this.noResultsLabel = computed(() => `${this.t('noResults')} "${this.query()}"`);
48668
- if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$6)) {
48669
- this.i18n.registerContent(DEFAULT_NAMESPACE$6, TRANSFER_OWNERSHIP_MODAL_I18N);
48648
+ if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$4)) {
48649
+ this.i18n.registerContent(DEFAULT_NAMESPACE$4, TRANSFER_OWNERSHIP_MODAL_I18N);
48670
48650
  }
48671
48651
  }
48672
48652
  ngOnInit() {
@@ -48851,849 +48831,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
48851
48831
  type: Input
48852
48832
  }] } });
48853
48833
 
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
-
49697
48834
  /**
49698
48835
  * Defaults i18n (es/en) embebidos en `val-organization-view`. Auto-registrados en
49699
48836
  * el constructor del componente si el consumer no proveyó el namespace
@@ -49749,8 +48886,6 @@ const ORGANIZATION_VIEW_I18N = {
49749
48886
  inviteCtaTitle: '¿Deseas agregar a alguien?',
49750
48887
  inviteCtaHint: 'Invita a usuarios a unirse a tu organización.',
49751
48888
  inviteOpen: 'Invitar',
49752
- importOpen: 'Importar',
49753
- apiKeysOpen: 'API Keys',
49754
48889
  viewMemberCta: 'Ver miembro',
49755
48890
  roleViewer: 'Visor',
49756
48891
  roleEditor: 'Editor',
@@ -49820,8 +48955,6 @@ const ORGANIZATION_VIEW_I18N = {
49820
48955
  inviteCtaTitle: 'Want to add someone?',
49821
48956
  inviteCtaHint: 'Invite users to join your organization.',
49822
48957
  inviteOpen: 'Invite',
49823
- importOpen: 'Import',
49824
- apiKeysOpen: 'API Keys',
49825
48958
  viewMemberCta: 'View member',
49826
48959
  roleViewer: 'Viewer',
49827
48960
  roleEditor: 'Editor',
@@ -49935,22 +49068,6 @@ class OrganizationViewComponent {
49935
49068
  this.members = signal([]);
49936
49069
  this.membersLoading = signal(false);
49937
49070
  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
- });
49954
49071
  this.membersNextToken = signal(null);
49955
49072
  this.loadingMoreMembers = signal(false);
49956
49073
  this.showAllMembers = signal(false);
@@ -50037,22 +49154,6 @@ class OrganizationViewComponent {
50037
49154
  shape: 'round',
50038
49155
  type: 'button',
50039
49156
  }));
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
- }));
50056
49157
  /** Guarda para detectar el primer disparo del effect (evita doble carga). */
50057
49158
  this.lastLoadedOrgId = null;
50058
49159
  const ns = this.ns;
@@ -50153,36 +49254,6 @@ class OrganizationViewComponent {
50153
49254
  backdropDismiss: true,
50154
49255
  });
50155
49256
  }
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
- }
50186
49257
  onViewPermissions() {
50187
49258
  this.nav.navigateByUrl(this.resolvedConfig().viewPermissionsRoute);
50188
49259
  }
@@ -50264,7 +49335,6 @@ class OrganizationViewComponent {
50264
49335
  if (!orgId)
50265
49336
  return Promise.resolve();
50266
49337
  this.membersLoading.set(true);
50267
- this.membersLoadError.set(null);
50268
49338
  this.membersNextToken.set(null);
50269
49339
  this.showAllMembers.set(false);
50270
49340
  return new Promise(resolve => {
@@ -50475,8 +49545,6 @@ class OrganizationViewComponent {
50475
49545
  <div class="section-body">
50476
49546
  @if (membersLoading()) {
50477
49547
  <val-skeleton-layout [props]="{ preset: 'list', rows: 3 }" />
50478
- } @else if (membersErrorState()) {
50479
- <val-empty-state [props]="membersErrorState()!" />
50480
49548
  } @else if (members().length === 0) {
50481
49549
  <val-text
50482
49550
  [props]="{
@@ -50569,8 +49637,6 @@ class OrganizationViewComponent {
50569
49637
  />
50570
49638
  </div>
50571
49639
  <val-button [props]="openInviteButtonProps()" (click)="onOpenInvite()" />
50572
- <val-button [props]="openImportButtonProps()" (click)="onOpenImport()" />
50573
- <val-button [props]="openApiKeysButtonProps()" (click)="onOpenApiKeys()" />
50574
49640
  </div>
50575
49641
  </section>
50576
49642
  }
@@ -50679,8 +49745,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
50679
49745
  ButtonComponent,
50680
49746
  MemberCardComponent,
50681
49747
  TransferOwnershipModalComponent,
50682
- MemberImportModalComponent,
50683
- ApiKeysModalComponent,
50684
49748
  ], template: `
50685
49749
  <div class="page">
50686
49750
  <header class="page-header">
@@ -50759,8 +49823,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
50759
49823
  <div class="section-body">
50760
49824
  @if (membersLoading()) {
50761
49825
  <val-skeleton-layout [props]="{ preset: 'list', rows: 3 }" />
50762
- } @else if (membersErrorState()) {
50763
- <val-empty-state [props]="membersErrorState()!" />
50764
49826
  } @else if (members().length === 0) {
50765
49827
  <val-text
50766
49828
  [props]="{
@@ -50853,8 +49915,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
50853
49915
  />
50854
49916
  </div>
50855
49917
  <val-button [props]="openInviteButtonProps()" (click)="onOpenInvite()" />
50856
- <val-button [props]="openImportButtonProps()" (click)="onOpenImport()" />
50857
- <val-button [props]="openApiKeysButtonProps()" (click)="onOpenApiKeys()" />
50858
49918
  </div>
50859
49919
  </section>
50860
49920
  }
@@ -52779,6 +51839,7 @@ const PERMISSIONS_VIEW_I18N = {
52779
51839
  noPermissions: 'Sin permisos asignados.',
52780
51840
  // Grupos de alcance
52781
51841
  groupApp: 'Permisos de la app',
51842
+ groupOrg: 'Permisos de la organización',
52782
51843
  groupPlatform: 'Permisos de plataforma',
52783
51844
  groupOther: 'Otras apps',
52784
51845
  // Roles del sistema
@@ -52834,6 +51895,7 @@ const PERMISSIONS_VIEW_I18N = {
52834
51895
  noCustomRoles: 'This organization has no custom roles.',
52835
51896
  noPermissions: 'No permissions assigned.',
52836
51897
  groupApp: 'App permissions',
51898
+ groupOrg: 'Organization permissions',
52837
51899
  groupPlatform: 'Platform permissions',
52838
51900
  groupOther: 'Other apps',
52839
51901
  roleViewer: 'Viewer',
@@ -52975,12 +52037,17 @@ class PermissionsViewComponent {
52975
52037
  }
52976
52038
  buildRoleView(role, byResource, currentAppId) {
52977
52039
  const appPerms = [];
52978
- const platformPerms = [];
52040
+ const orgPerms = [];
52979
52041
  const otherByApp = new Map();
52980
52042
  for (const perm of role.permissions) {
52981
52043
  const [resource, action] = perm.split(':');
52982
- const label = this.permLabel(resource, action);
52983
52044
  const entry = byResource.get(resource);
52045
+ // internal (operación de plataforma) NUNCA se muestra a nivel de producto
52046
+ // — defensa en profundidad junto al filtro del backend (ADR-024).
52047
+ if (entry?.scope === 'internal') {
52048
+ continue;
52049
+ }
52050
+ const label = this.permLabel(resource, action);
52984
52051
  if (entry?.scope === 'app') {
52985
52052
  if (entry.appId === currentAppId) {
52986
52053
  appPerms.push(label);
@@ -52993,19 +52060,19 @@ class PermissionsViewComponent {
52993
52060
  }
52994
52061
  }
52995
52062
  else {
52996
- // global o desconocido → plataforma
52997
- platformPerms.push(label);
52063
+ // org o desconocido → gestión de la organización del cliente
52064
+ orgPerms.push(label);
52998
52065
  }
52999
52066
  }
53000
52067
  const groups = [];
53001
52068
  if (appPerms.length) {
53002
52069
  groups.push({ key: 'app', label: this.tt('groupApp'), perms: appPerms });
53003
52070
  }
53004
- if (platformPerms.length) {
52071
+ if (orgPerms.length) {
53005
52072
  groups.push({
53006
- key: 'platform',
53007
- label: this.tt('groupPlatform'),
53008
- perms: platformPerms,
52073
+ key: 'org',
52074
+ label: this.tt('groupOrg'),
52075
+ perms: orgPerms,
53009
52076
  });
53010
52077
  }
53011
52078
  for (const [appId, perms] of otherByApp) {
@@ -63602,5 +62669,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
63602
62669
  * Generated bundle index. Do not edit.
63603
62670
  */
63604
62671
 
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 };
62672
+ 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 };
63606
62673
  //# sourceMappingURL=valtech-components.mjs.map