valtech-components 2.0.1015 → 2.0.1017

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 (24) hide show
  1. package/esm2022/lib/components/molecules/multi-select-search/multi-select-search.component.mjs +14 -37
  2. package/esm2022/lib/components/molecules/permission-selector/permission-selector.component.mjs +156 -0
  3. package/esm2022/lib/components/molecules/permission-selector/types.mjs +2 -0
  4. package/esm2022/lib/components/molecules/select-search/select-search.component.mjs +3 -15
  5. package/esm2022/lib/components/organisms/about-view/about-view.component.mjs +9 -4
  6. package/esm2022/lib/components/organisms/api-keys-modal/api-keys-modal.component.mjs +59 -35
  7. package/esm2022/lib/components/organisms/invite-member-modal/invite-member-modal.component.mjs +7 -6
  8. package/esm2022/lib/components/organisms/org-info-sheet/org-info-sheet.component.mjs +52 -40
  9. package/esm2022/lib/components/organisms/organization-view/organization-view.component.mjs +54 -18
  10. package/esm2022/lib/components/organisms/organization-view/organization-view.i18n.mjs +11 -3
  11. package/esm2022/lib/services/org/permission-catalog.service.mjs +1 -1
  12. package/esm2022/lib/version.mjs +2 -2
  13. package/esm2022/public-api.mjs +3 -1
  14. package/fesm2022/valtech-components.mjs +382 -182
  15. package/fesm2022/valtech-components.mjs.map +1 -1
  16. package/lib/components/molecules/permission-selector/permission-selector.component.d.ts +46 -0
  17. package/lib/components/molecules/permission-selector/types.d.ts +15 -0
  18. package/lib/components/organisms/api-keys-modal/api-keys-modal.component.d.ts +17 -1
  19. package/lib/components/organisms/org-info-sheet/org-info-sheet.component.d.ts +7 -1
  20. package/lib/components/organisms/organization-view/organization-view.component.d.ts +1 -0
  21. package/lib/services/org/permission-catalog.service.d.ts +6 -0
  22. package/lib/version.d.ts +1 -1
  23. package/package.json +1 -1
  24. package/public-api.d.ts +2 -0
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { inject, InjectionToken, makeEnvironmentProviders, APP_INITIALIZER, ErrorHandler, Injectable, DestroyRef, Inject, signal, computed, PLATFORM_ID, isDevMode, Optional, runInInjectionContext, effect, Pipe, TemplateRef, ViewContainerRef, input, Directive, EventEmitter, Component, Input, Output, ChangeDetectionStrategy, HostBinding, HostListener, NgZone, ViewChild, ChangeDetectorRef, output, ContentChild, ElementRef, viewChild, untracked, Injector, isSignal, ViewEncapsulation } from '@angular/core';
2
+ import { inject, InjectionToken, makeEnvironmentProviders, APP_INITIALIZER, ErrorHandler, Injectable, DestroyRef, Inject, signal, computed, PLATFORM_ID, isDevMode, Optional, runInInjectionContext, effect, Pipe, TemplateRef, ViewContainerRef, input, Directive, EventEmitter, Component, Input, Output, ChangeDetectionStrategy, HostBinding, HostListener, NgZone, ViewChild, ChangeDetectorRef, output, ContentChild, model, ElementRef, viewChild, untracked, Injector, isSignal, ViewEncapsulation } from '@angular/core';
3
3
  import { BehaviorSubject, throwError, Subject, map, distinctUntilChanged, filter as filter$1, take as take$1, firstValueFrom, of, from, EMPTY, Observable, interval, debounceTime, switchMap as switchMap$1, catchError as catchError$1, takeUntil, isObservable, forkJoin, race, timer, shareReplay } from 'rxjs';
4
4
  import { catchError, switchMap, finalize, filter, take, map as map$1, tap, distinctUntilChanged as distinctUntilChanged$1, retry, timeout, debounceTime as debounceTime$1, takeUntil as takeUntil$1 } from 'rxjs/operators';
5
5
  import * as i1$3 from '@angular/common/http';
@@ -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.1015';
57
+ const VERSION = '2.0.1017';
58
58
 
59
59
  // Control de estado de refresco (singleton a nivel de módulo)
60
60
  let isRefreshing = false;
@@ -18915,13 +18915,7 @@ class SelectSearchComponent {
18915
18915
 
18916
18916
  <ion-input style="position: absolute;" [formControl]="props.control" type="hidden"></ion-input>
18917
18917
 
18918
- <ion-modal
18919
- #modal
18920
- trigger="open-modal"
18921
- [initialBreakpoint]="1"
18922
- [breakpoints]="[0, 0.5, 0.75, 1]"
18923
- (didDismiss)="resetState()"
18924
- >
18918
+ <ion-modal #modal [initialBreakpoint]="1" [breakpoints]="[0, 0.5, 0.75, 1]" (didDismiss)="resetState()">
18925
18919
  <ng-template>
18926
18920
  <ion-header>
18927
18921
  <ion-toolbar>
@@ -18962,13 +18956,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
18962
18956
 
18963
18957
  <ion-input style="position: absolute;" [formControl]="props.control" type="hidden"></ion-input>
18964
18958
 
18965
- <ion-modal
18966
- #modal
18967
- trigger="open-modal"
18968
- [initialBreakpoint]="1"
18969
- [breakpoints]="[0, 0.5, 0.75, 1]"
18970
- (didDismiss)="resetState()"
18971
- >
18959
+ <ion-modal #modal [initialBreakpoint]="1" [breakpoints]="[0, 0.5, 0.75, 1]" (didDismiss)="resetState()">
18972
18960
  <ng-template>
18973
18961
  <ion-header>
18974
18962
  <ion-toolbar>
@@ -22052,8 +22040,13 @@ class MultiSelectSearchComponent {
22052
22040
  }
22053
22041
  // Parse comma-separated values for multi-select
22054
22042
  const values = typeof controlValue === 'string'
22055
- ? controlValue.split(',').map(v => v.trim()).filter(v => v !== '')
22056
- : Array.isArray(controlValue) ? controlValue : [controlValue];
22043
+ ? controlValue
22044
+ .split(',')
22045
+ .map(v => v.trim())
22046
+ .filter(v => v !== '')
22047
+ : Array.isArray(controlValue)
22048
+ ? controlValue
22049
+ : [controlValue];
22057
22050
  if (this.props.options && this.props.options.length > 0) {
22058
22051
  const map = new Map(this.props.options.map(opt => [String(opt[this.valueProperty]), opt]));
22059
22052
  this.selectedItems = values.map(val => map.get(String(val))).filter(Boolean);
@@ -22215,13 +22208,7 @@ class MultiSelectSearchComponent {
22215
22208
 
22216
22209
  <ion-input style="position: absolute;" [formControl]="props.control" type="hidden"></ion-input>
22217
22210
 
22218
- <ion-modal
22219
- #modal
22220
- trigger="open-modal"
22221
- [initialBreakpoint]="1"
22222
- [breakpoints]="[0, 0.5, 0.75, 1]"
22223
- (didDismiss)="resetState()"
22224
- >
22211
+ <ion-modal #modal [initialBreakpoint]="1" [breakpoints]="[0, 0.5, 0.75, 1]" (didDismiss)="resetState()">
22225
22212
  <ng-template>
22226
22213
  <ion-header>
22227
22214
  <ion-toolbar>
@@ -22237,12 +22224,7 @@ class MultiSelectSearchComponent {
22237
22224
  <ion-content>
22238
22225
  <!-- Action buttons for multi-select -->
22239
22226
  <div class="actions-container" style="padding: 16px; border-bottom: 1px solid var(--ion-color-light-shade);">
22240
- <ion-button
22241
- fill="clear"
22242
- size="small"
22243
- (click)="selectAll()"
22244
- [disabled]="filteredItems.length === 0"
22245
- >
22227
+ <ion-button fill="clear" size="small" (click)="selectAll()" [disabled]="filteredItems.length === 0">
22246
22228
  {{ getSelectAllText() }}
22247
22229
  </ion-button>
22248
22230
  <ion-button
@@ -22258,10 +22240,7 @@ class MultiSelectSearchComponent {
22258
22240
 
22259
22241
  <ion-list>
22260
22242
  <ion-item *ngFor="let item of filteredItems" button (click)="selectItem(item)" detail="false">
22261
- <ion-checkbox
22262
- slot="start"
22263
- [checked]="isItemSelected(item)"
22264
- ></ion-checkbox>
22243
+ <ion-checkbox slot="start" [checked]="isItemSelected(item)"></ion-checkbox>
22265
22244
  <ion-label>{{ item[labelProperty] }}</ion-label>
22266
22245
  </ion-item>
22267
22246
  <ion-item *ngIf="filteredItems.length === 0" lines="none">
@@ -22293,13 +22272,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
22293
22272
 
22294
22273
  <ion-input style="position: absolute;" [formControl]="props.control" type="hidden"></ion-input>
22295
22274
 
22296
- <ion-modal
22297
- #modal
22298
- trigger="open-modal"
22299
- [initialBreakpoint]="1"
22300
- [breakpoints]="[0, 0.5, 0.75, 1]"
22301
- (didDismiss)="resetState()"
22302
- >
22275
+ <ion-modal #modal [initialBreakpoint]="1" [breakpoints]="[0, 0.5, 0.75, 1]" (didDismiss)="resetState()">
22303
22276
  <ng-template>
22304
22277
  <ion-header>
22305
22278
  <ion-toolbar>
@@ -22315,12 +22288,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
22315
22288
  <ion-content>
22316
22289
  <!-- Action buttons for multi-select -->
22317
22290
  <div class="actions-container" style="padding: 16px; border-bottom: 1px solid var(--ion-color-light-shade);">
22318
- <ion-button
22319
- fill="clear"
22320
- size="small"
22321
- (click)="selectAll()"
22322
- [disabled]="filteredItems.length === 0"
22323
- >
22291
+ <ion-button fill="clear" size="small" (click)="selectAll()" [disabled]="filteredItems.length === 0">
22324
22292
  {{ getSelectAllText() }}
22325
22293
  </ion-button>
22326
22294
  <ion-button
@@ -22336,10 +22304,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
22336
22304
 
22337
22305
  <ion-list>
22338
22306
  <ion-item *ngFor="let item of filteredItems" button (click)="selectItem(item)" detail="false">
22339
- <ion-checkbox
22340
- slot="start"
22341
- [checked]="isItemSelected(item)"
22342
- ></ion-checkbox>
22307
+ <ion-checkbox slot="start" [checked]="isItemSelected(item)"></ion-checkbox>
22343
22308
  <ion-label>{{ item[labelProperty] }}</ion-label>
22344
22309
  </ion-item>
22345
22310
  <ion-item *ngIf="filteredItems.length === 0" lines="none">
@@ -28205,6 +28170,158 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
28205
28170
  type: Output
28206
28171
  }] } });
28207
28172
 
28173
+ const DEFAULT_NAMESPACE$j = 'PermissionSelector';
28174
+ const PERMISSION_SELECTOR_I18N = {
28175
+ es: {
28176
+ selectAll: 'Todos',
28177
+ empty: 'No hay permisos disponibles para asignar.',
28178
+ },
28179
+ en: {
28180
+ selectAll: 'All',
28181
+ empty: 'No permissions available to assign.',
28182
+ },
28183
+ };
28184
+ /**
28185
+ * `val-permission-selector` — selector de permisos agrupado por recurso.
28186
+ *
28187
+ * Presentacional: recibe el catálogo (YA filtrado por el consumer — sin `internal`,
28188
+ * sin otras apps) y emite los códigos `resource:action` seleccionados. Agrupa por
28189
+ * recurso con su label legible (del backend), una fila por recurso con checkbox
28190
+ * "Todos" (tri-estado) y un chip por acción. Bilingüe vía `locale` + los labels del
28191
+ * catálogo; las pocas etiquetas propias (Todos / vacío) se auto-registran (es/en).
28192
+ *
28193
+ * Pensado para el modal de creación de API Key (asignar permisos a la key) y
28194
+ * reusable en cualquier UI que asigne permisos (roles custom, etc.).
28195
+ */
28196
+ class PermissionSelectorComponent {
28197
+ constructor() {
28198
+ this.i18n = inject(I18nService);
28199
+ /** Catálogo YA filtrado (sin internal, sin otras apps). */
28200
+ this.permissions = input([]);
28201
+ /** Labels de acción (es/en) por código de acción — del catálogo del backend. */
28202
+ this.actionLabels = input({});
28203
+ /** Idioma activo (es/en). Si no se pasa, usa el de `I18nService`. */
28204
+ this.locale = input('');
28205
+ /** Permisos seleccionados (`resource:action`). Two-way (`model`). */
28206
+ this.selected = model([]);
28207
+ this.activeLocale = computed(() => this.locale() || this.i18n.lang());
28208
+ this.groups = computed(() => {
28209
+ const sel = new Set(this.selected());
28210
+ const loc = this.activeLocale();
28211
+ const actionLabels = this.actionLabels();
28212
+ return this.permissions().map(r => {
28213
+ const actions = r.actions.map(a => {
28214
+ const code = `${r.resource}:${a}`;
28215
+ return { action: a, code, label: actionLabels[a]?.[loc] ?? a, checked: sel.has(code) };
28216
+ });
28217
+ const checkedCount = actions.filter(a => a.checked).length;
28218
+ return {
28219
+ resource: r.resource,
28220
+ label: r.label?.[loc] ?? r.resource,
28221
+ actions,
28222
+ checkedCount,
28223
+ all: actions.length > 0 && checkedCount === actions.length,
28224
+ some: checkedCount > 0 && checkedCount < actions.length,
28225
+ };
28226
+ });
28227
+ });
28228
+ if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$j)) {
28229
+ this.i18n.registerContent(DEFAULT_NAMESPACE$j, PERMISSION_SELECTOR_I18N);
28230
+ }
28231
+ }
28232
+ toggleAction(code) {
28233
+ const sel = new Set(this.selected());
28234
+ if (sel.has(code))
28235
+ sel.delete(code);
28236
+ else
28237
+ sel.add(code);
28238
+ this.selected.set([...sel]);
28239
+ }
28240
+ toggleGroup(resource) {
28241
+ const group = this.groups().find(g => g.resource === resource);
28242
+ if (!group)
28243
+ return;
28244
+ const sel = new Set(this.selected());
28245
+ if (group.all)
28246
+ group.actions.forEach(a => sel.delete(a.code));
28247
+ else
28248
+ group.actions.forEach(a => sel.add(a.code));
28249
+ this.selected.set([...sel]);
28250
+ }
28251
+ tt(key) {
28252
+ this.i18n.lang();
28253
+ return this.i18n.t(key, DEFAULT_NAMESPACE$j);
28254
+ }
28255
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: PermissionSelectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
28256
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: PermissionSelectorComponent, isStandalone: true, selector: "val-permission-selector", inputs: { permissions: { classPropertyName: "permissions", publicName: "permissions", isSignal: true, isRequired: false, transformFunction: null }, actionLabels: { classPropertyName: "actionLabels", publicName: "actionLabels", isSignal: true, isRequired: false, transformFunction: null }, locale: { classPropertyName: "locale", publicName: "locale", isSignal: true, isRequired: false, transformFunction: null }, selected: { classPropertyName: "selected", publicName: "selected", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selected: "selectedChange" }, ngImport: i0, template: `
28257
+ @if (groups().length === 0) {
28258
+ <val-text [props]="{ content: tt('empty'), size: 'medium', color: 'medium', bold: false }" />
28259
+ } @else {
28260
+ <div class="perm-selector">
28261
+ @for (g of groups(); track g.resource) {
28262
+ <div class="perm-group" [class.perm-group--active]="g.checkedCount > 0">
28263
+ <div class="perm-group__head">
28264
+ <label class="perm-group__all">
28265
+ <input type="checkbox" [checked]="g.all" [indeterminate]="g.some" (change)="toggleGroup(g.resource)" />
28266
+ <span class="perm-group__title">{{ g.label }}</span>
28267
+ </label>
28268
+ <span class="perm-group__count">{{ g.checkedCount }}/{{ g.actions.length }}</span>
28269
+ </div>
28270
+ <div class="perm-group__actions">
28271
+ @for (a of g.actions; track a.code) {
28272
+ <button
28273
+ type="button"
28274
+ class="perm-chip"
28275
+ [class.perm-chip--on]="a.checked"
28276
+ [attr.aria-pressed]="a.checked"
28277
+ (click)="toggleAction(a.code)"
28278
+ >
28279
+ {{ a.label }}
28280
+ </button>
28281
+ }
28282
+ </div>
28283
+ </div>
28284
+ }
28285
+ </div>
28286
+ }
28287
+ `, isInline: true, styles: [":host{display:block}.perm-selector{display:flex;flex-direction:column;gap:10px}.perm-group{border:1.5px solid var(--ion-color-light, #e7e4ea);border-radius:12px;padding:12px 14px;transition:border-color .15s ease}.perm-group--active{border-color:var(--ion-color-primary, #7026df)}.perm-group__head{display:flex;align-items:center;justify-content:space-between;gap:8px}.perm-group__all{display:flex;align-items:center;gap:10px;cursor:pointer;font-weight:600;color:var(--ion-color-dark)}.perm-group__all input{width:18px;height:18px;accent-color:var(--ion-color-primary, #7026df);cursor:pointer}.perm-group__title{font-size:.95rem}.perm-group__count{font-size:.75rem;color:var(--ion-color-medium, #6b6675);flex-shrink:0}.perm-group__actions{display:flex;flex-wrap:wrap;gap:8px;margin-top:10px}.perm-chip{appearance:none;border:1.5px solid var(--ion-color-medium, #6b6675);background:transparent;color:var(--ion-color-dark);border-radius:999px;padding:5px 14px;font-size:.8125rem;cursor:pointer;transition:background .15s ease,border-color .15s ease,color .15s ease}.perm-chip:hover{border-color:var(--ion-color-primary, #7026df)}.perm-chip--on{background:var(--ion-color-primary, #7026df);border-color:var(--ion-color-primary, #7026df);color:var(--ion-color-primary-contrast, #fff)}:host-context(body.dark) .perm-group,:host-context(html.ion-palette-dark) .perm-group{border-color:#ffffff1f}\n"], dependencies: [{ kind: "component", type: TextComponent, selector: "val-text", inputs: ["props"] }] }); }
28288
+ }
28289
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: PermissionSelectorComponent, decorators: [{
28290
+ type: Component,
28291
+ args: [{ selector: 'val-permission-selector', standalone: true, imports: [TextComponent], template: `
28292
+ @if (groups().length === 0) {
28293
+ <val-text [props]="{ content: tt('empty'), size: 'medium', color: 'medium', bold: false }" />
28294
+ } @else {
28295
+ <div class="perm-selector">
28296
+ @for (g of groups(); track g.resource) {
28297
+ <div class="perm-group" [class.perm-group--active]="g.checkedCount > 0">
28298
+ <div class="perm-group__head">
28299
+ <label class="perm-group__all">
28300
+ <input type="checkbox" [checked]="g.all" [indeterminate]="g.some" (change)="toggleGroup(g.resource)" />
28301
+ <span class="perm-group__title">{{ g.label }}</span>
28302
+ </label>
28303
+ <span class="perm-group__count">{{ g.checkedCount }}/{{ g.actions.length }}</span>
28304
+ </div>
28305
+ <div class="perm-group__actions">
28306
+ @for (a of g.actions; track a.code) {
28307
+ <button
28308
+ type="button"
28309
+ class="perm-chip"
28310
+ [class.perm-chip--on]="a.checked"
28311
+ [attr.aria-pressed]="a.checked"
28312
+ (click)="toggleAction(a.code)"
28313
+ >
28314
+ {{ a.label }}
28315
+ </button>
28316
+ }
28317
+ </div>
28318
+ </div>
28319
+ }
28320
+ </div>
28321
+ }
28322
+ `, styles: [":host{display:block}.perm-selector{display:flex;flex-direction:column;gap:10px}.perm-group{border:1.5px solid var(--ion-color-light, #e7e4ea);border-radius:12px;padding:12px 14px;transition:border-color .15s ease}.perm-group--active{border-color:var(--ion-color-primary, #7026df)}.perm-group__head{display:flex;align-items:center;justify-content:space-between;gap:8px}.perm-group__all{display:flex;align-items:center;gap:10px;cursor:pointer;font-weight:600;color:var(--ion-color-dark)}.perm-group__all input{width:18px;height:18px;accent-color:var(--ion-color-primary, #7026df);cursor:pointer}.perm-group__title{font-size:.95rem}.perm-group__count{font-size:.75rem;color:var(--ion-color-medium, #6b6675);flex-shrink:0}.perm-group__actions{display:flex;flex-wrap:wrap;gap:8px;margin-top:10px}.perm-chip{appearance:none;border:1.5px solid var(--ion-color-medium, #6b6675);background:transparent;color:var(--ion-color-dark);border-radius:999px;padding:5px 14px;font-size:.8125rem;cursor:pointer;transition:background .15s ease,border-color .15s ease,color .15s ease}.perm-chip:hover{border-color:var(--ion-color-primary, #7026df)}.perm-chip--on{background:var(--ion-color-primary, #7026df);border-color:var(--ion-color-primary, #7026df);color:var(--ion-color-primary-contrast, #fff)}:host-context(body.dark) .perm-group,:host-context(html.ion-palette-dark) .perm-group{border-color:#ffffff1f}\n"] }]
28323
+ }], ctorParameters: () => [] });
28324
+
28208
28325
  addIcons({ checkmarkCircle, closeCircle, alertCircle });
28209
28326
  /**
28210
28327
  * Username Input Component
@@ -46129,6 +46246,12 @@ class OrgInfoSheetComponent {
46129
46246
  this.body = '';
46130
46247
  /** Label del botón cerrar (resuelto por el consumer). Fallback al namespace propio. */
46131
46248
  this.closeLabel = '';
46249
+ /**
46250
+ * Muestra las 3 feature-cards de organizaciones (colaboración / cambio rápido /
46251
+ * invitaciones). `false` → sheet article-style (solo título + subtítulo + cuerpo),
46252
+ * reusable como explainer genérico (ej. integraciones / API keys). Default `true`.
46253
+ */
46254
+ this.showFeatures = true;
46132
46255
  /** Props del botón cerrar del footer (el consumer puede pasar custom). */
46133
46256
  this.closeProps = {
46134
46257
  text: 'Cerrar',
@@ -46158,7 +46281,7 @@ class OrgInfoSheetComponent {
46158
46281
  return this.i18n.t(key, DEFAULT_NAMESPACE$d);
46159
46282
  }
46160
46283
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: OrgInfoSheetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
46161
- 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: `
46284
+ 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", showFeatures: "showFeatures", closeProps: "closeProps", _modalRef: "_modalRef" }, ngImport: i0, template: `
46162
46285
  <ion-header>
46163
46286
  <ion-toolbar>
46164
46287
  <ion-buttons slot="end">
@@ -46173,29 +46296,31 @@ class OrgInfoSheetComponent {
46173
46296
  <val-display [props]="{ content: resolvedTitle(), size: 'small', color: 'dark' }" />
46174
46297
  <val-title [props]="{ content: resolvedSubtitle(), size: 'large', color: '', bold: false }" />
46175
46298
 
46176
- <div class="info-features">
46177
- <div class="info-feature">
46178
- <ion-icon name="people-outline" class="info-feature__icon" />
46179
- <div>
46180
- <div class="info-feature__title">{{ tt('featureCollabTitle') }}</div>
46181
- <div class="info-feature__desc">{{ tt('featureCollabDesc') }}</div>
46299
+ @if (showFeatures) {
46300
+ <div class="info-features">
46301
+ <div class="info-feature">
46302
+ <ion-icon name="people-outline" class="info-feature__icon" />
46303
+ <div>
46304
+ <div class="info-feature__title">{{ tt('featureCollabTitle') }}</div>
46305
+ <div class="info-feature__desc">{{ tt('featureCollabDesc') }}</div>
46306
+ </div>
46182
46307
  </div>
46183
- </div>
46184
- <div class="info-feature">
46185
- <ion-icon name="swap-horizontal-outline" class="info-feature__icon" />
46186
- <div>
46187
- <div class="info-feature__title">{{ tt('featureSwitchTitle') }}</div>
46188
- <div class="info-feature__desc">{{ tt('featureSwitchDesc') }}</div>
46308
+ <div class="info-feature">
46309
+ <ion-icon name="swap-horizontal-outline" class="info-feature__icon" />
46310
+ <div>
46311
+ <div class="info-feature__title">{{ tt('featureSwitchTitle') }}</div>
46312
+ <div class="info-feature__desc">{{ tt('featureSwitchDesc') }}</div>
46313
+ </div>
46189
46314
  </div>
46190
- </div>
46191
- <div class="info-feature">
46192
- <ion-icon name="mail-outline" class="info-feature__icon" />
46193
- <div>
46194
- <div class="info-feature__title">{{ tt('featureInviteTitle') }}</div>
46195
- <div class="info-feature__desc">{{ tt('featureInviteDesc') }}</div>
46315
+ <div class="info-feature">
46316
+ <ion-icon name="mail-outline" class="info-feature__icon" />
46317
+ <div>
46318
+ <div class="info-feature__title">{{ tt('featureInviteTitle') }}</div>
46319
+ <div class="info-feature__desc">{{ tt('featureInviteDesc') }}</div>
46320
+ </div>
46196
46321
  </div>
46197
46322
  </div>
46198
- </div>
46323
+ }
46199
46324
 
46200
46325
  @for (para of paragraphs(); track $index) {
46201
46326
  <val-text
@@ -46242,29 +46367,31 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
46242
46367
  <val-display [props]="{ content: resolvedTitle(), size: 'small', color: 'dark' }" />
46243
46368
  <val-title [props]="{ content: resolvedSubtitle(), size: 'large', color: '', bold: false }" />
46244
46369
 
46245
- <div class="info-features">
46246
- <div class="info-feature">
46247
- <ion-icon name="people-outline" class="info-feature__icon" />
46248
- <div>
46249
- <div class="info-feature__title">{{ tt('featureCollabTitle') }}</div>
46250
- <div class="info-feature__desc">{{ tt('featureCollabDesc') }}</div>
46370
+ @if (showFeatures) {
46371
+ <div class="info-features">
46372
+ <div class="info-feature">
46373
+ <ion-icon name="people-outline" class="info-feature__icon" />
46374
+ <div>
46375
+ <div class="info-feature__title">{{ tt('featureCollabTitle') }}</div>
46376
+ <div class="info-feature__desc">{{ tt('featureCollabDesc') }}</div>
46377
+ </div>
46251
46378
  </div>
46252
- </div>
46253
- <div class="info-feature">
46254
- <ion-icon name="swap-horizontal-outline" class="info-feature__icon" />
46255
- <div>
46256
- <div class="info-feature__title">{{ tt('featureSwitchTitle') }}</div>
46257
- <div class="info-feature__desc">{{ tt('featureSwitchDesc') }}</div>
46379
+ <div class="info-feature">
46380
+ <ion-icon name="swap-horizontal-outline" class="info-feature__icon" />
46381
+ <div>
46382
+ <div class="info-feature__title">{{ tt('featureSwitchTitle') }}</div>
46383
+ <div class="info-feature__desc">{{ tt('featureSwitchDesc') }}</div>
46384
+ </div>
46258
46385
  </div>
46259
- </div>
46260
- <div class="info-feature">
46261
- <ion-icon name="mail-outline" class="info-feature__icon" />
46262
- <div>
46263
- <div class="info-feature__title">{{ tt('featureInviteTitle') }}</div>
46264
- <div class="info-feature__desc">{{ tt('featureInviteDesc') }}</div>
46386
+ <div class="info-feature">
46387
+ <ion-icon name="mail-outline" class="info-feature__icon" />
46388
+ <div>
46389
+ <div class="info-feature__title">{{ tt('featureInviteTitle') }}</div>
46390
+ <div class="info-feature__desc">{{ tt('featureInviteDesc') }}</div>
46391
+ </div>
46265
46392
  </div>
46266
46393
  </div>
46267
- </div>
46394
+ }
46268
46395
 
46269
46396
  @for (para of paragraphs(); track $index) {
46270
46397
  <val-text
@@ -46290,6 +46417,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
46290
46417
  type: Input
46291
46418
  }], closeLabel: [{
46292
46419
  type: Input
46420
+ }], showFeatures: [{
46421
+ type: Input
46293
46422
  }], closeProps: [{
46294
46423
  type: Input
46295
46424
  }], _modalRef: [{
@@ -47812,7 +47941,11 @@ class InviteMemberModalComponent {
47812
47941
  return [];
47813
47942
  }
47814
47943
  const cleanQ = (q.startsWith('@') ? q.slice(1) : q).toLowerCase();
47815
- const q$ = query$1(collection(this.firestore, 'profiles'), orderBy('handle'), startAt(cleanQ), endAt(cleanQ + ''), limit(8));
47944
+ // Prefijo: el sentinel alto `` cierra el rango (antes `cleanQ + ''`
47945
+ // era exact-match → solo encontraba el handle idéntico). NOTA: solo busca
47946
+ // por `handle`; la búsqueda por nombre requiere un campo normalizado en
47947
+ // /profiles (pendiente, ver TODO de búsqueda por nombre).
47948
+ const q$ = query$1(collection(this.firestore, 'profiles'), orderBy('handle'), startAt(cleanQ), endAt(cleanQ + '\uf8ff'), limit(8));
47816
47949
  return from(getDocs(q$)).pipe(map$1(snap => ({
47817
47950
  users: snap.docs.map(d => ({
47818
47951
  userId: d.id,
@@ -47825,10 +47958,7 @@ class InviteMemberModalComponent {
47825
47958
  next: res => {
47826
47959
  if (res && 'users' in res) {
47827
47960
  const currentUserId = this.auth.user()?.userId;
47828
- const excluded = new Set([
47829
- ...(currentUserId ? [currentUserId] : []),
47830
- ...this.existingMemberIds,
47831
- ]);
47961
+ const excluded = new Set([...(currentUserId ? [currentUserId] : []), ...this.existingMemberIds]);
47832
47962
  this.searchResults.set(res.users.filter(u => !excluded.has(u.userId)));
47833
47963
  }
47834
47964
  this.searching.set(false);
@@ -49328,6 +49458,35 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
49328
49458
  args: [VALTECH_AUTH_CONFIG]
49329
49459
  }] }, { type: i1$3.HttpClient }] });
49330
49460
 
49461
+ /**
49462
+ * Consume el catálogo de permisos del backend (`GET /org/{orgId}/permissions-catalog`).
49463
+ * El backend es la fuente de verdad de qué permisos existen y de su alcance
49464
+ * (globales de plataforma vs. propios de una app). La respuesta incluye el
49465
+ * `appId` del request para que la vista distinga la app actual.
49466
+ *
49467
+ * Promovido desde `showcase` a la lib bajo el proceso de ADR-021. Usa
49468
+ * `VALTECH_AUTH_CONFIG.apiUrl` (como `OrgService`) en vez del `environment` de la
49469
+ * app — la lib no importa el environment del consumer.
49470
+ */
49471
+ class PermissionCatalogService {
49472
+ constructor(config, http) {
49473
+ this.config = config;
49474
+ this.http = http;
49475
+ }
49476
+ getCatalog(orgId) {
49477
+ return this.http.get(`${this.config.apiUrl}/org/${orgId}/permissions-catalog`);
49478
+ }
49479
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: PermissionCatalogService, deps: [{ token: VALTECH_AUTH_CONFIG }, { token: i1$3.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable }); }
49480
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: PermissionCatalogService, providedIn: 'root' }); }
49481
+ }
49482
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: PermissionCatalogService, decorators: [{
49483
+ type: Injectable,
49484
+ args: [{ providedIn: 'root' }]
49485
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
49486
+ type: Inject,
49487
+ args: [VALTECH_AUTH_CONFIG]
49488
+ }] }, { type: i1$3.HttpClient }] });
49489
+
49331
49490
  /**
49332
49491
  * Defaults i18n (es/en) embebidos en `val-api-keys-modal` (ADR-023).
49333
49492
  * Auto-registrados si el consumer no proveyó el namespace `Settings.ApiKeysModal`.
@@ -49403,10 +49562,17 @@ class ApiKeysModalComponent {
49403
49562
  constructor() {
49404
49563
  this.i18n = inject(I18nService);
49405
49564
  this.apiKeys = inject(ApiKeyService);
49565
+ this.catalog = inject(PermissionCatalogService);
49566
+ this.auth = inject(AuthService);
49406
49567
  /** Namespace i18n. */
49407
49568
  this.i18nNamespace = DEFAULT_NAMESPACE$5;
49569
+ /** Permisos del catálogo YA filtrados (sin internal, sin otras apps). */
49570
+ this.catalogPermissions = signal([]);
49571
+ /** Labels de acción (es/en) del backend, para el selector. */
49572
+ this.actionLabels = signal({});
49573
+ /** Permisos seleccionados para la nueva key (`resource:action`). */
49574
+ this.selectedPerms = signal([]);
49408
49575
  this.keys = signal([]);
49409
- this.availablePermissions = signal([]);
49410
49576
  this.loading = signal(true);
49411
49577
  this.showCreate = signal(false);
49412
49578
  this.submitting = signal(false);
@@ -49434,24 +49600,6 @@ class ApiKeysModalComponent {
49434
49600
  value: '',
49435
49601
  state: ComponentStates.ENABLED,
49436
49602
  };
49437
- const permsField = {
49438
- token: 'apikey-perms',
49439
- name: 'permissions',
49440
- label: this.t('permissionsLabel'),
49441
- hint: '',
49442
- placeholder: '',
49443
- type: InputType.MULTI_SELECT,
49444
- order: 2,
49445
- validators: [Validators.required],
49446
- errors: { required: this.t('noPerms') },
49447
- value: '',
49448
- options: this.availablePermissions().map((p, i) => ({
49449
- id: p,
49450
- name: p,
49451
- order: i + 1,
49452
- })),
49453
- state: ComponentStates.ENABLED,
49454
- };
49455
49603
  const expiresField = {
49456
49604
  token: 'apikey-expires',
49457
49605
  name: 'expiresInDays',
@@ -49479,7 +49627,7 @@ class ApiKeysModalComponent {
49479
49627
  return {
49480
49628
  name: '',
49481
49629
  state: working ? ComponentStates.WORKING : ComponentStates.ENABLED,
49482
- sections: [{ name: '', order: 1, fields: [nameField, permsField, expiresField] }],
49630
+ sections: [{ name: '', order: 1, fields: [nameField, expiresField] }],
49483
49631
  actions: submitButton,
49484
49632
  };
49485
49633
  });
@@ -49492,11 +49640,34 @@ class ApiKeysModalComponent {
49492
49640
  this.i18n.registerContent(this.i18nNamespace, API_KEYS_MODAL_I18N);
49493
49641
  }
49494
49642
  this.refresh();
49495
- this.apiKeys.availablePermissions().subscribe({
49496
- next: perms => this.availablePermissions.set(perms),
49497
- error: () => { },
49643
+ this.loadCatalog();
49644
+ }
49645
+ /**
49646
+ * Carga el catálogo de permisos y lo filtra a lo asignable a una API Key de la
49647
+ * org: descarta `scope:internal` (el backend ya los omite para no-staff) y los
49648
+ * permisos de OTRAS apps (`appId !== appId del catálogo`). Quedan los permisos de
49649
+ * org (users/roles/rbac) + los de la app actual.
49650
+ */
49651
+ loadCatalog() {
49652
+ const orgId = this.activeOrgId();
49653
+ if (!orgId)
49654
+ return;
49655
+ this.catalog.getCatalog(orgId).subscribe({
49656
+ next: res => {
49657
+ const appId = res.appId;
49658
+ const filtered = (res.permissions ?? []).filter(p => p.scope !== 'internal' && (p.scope !== 'app' || p.appId === appId));
49659
+ this.catalogPermissions.set(filtered);
49660
+ this.actionLabels.set(res.actionLabels ?? {});
49661
+ },
49662
+ error: () => {
49663
+ // Sin catálogo: el selector queda vacío (el form valida ≥1 permiso).
49664
+ },
49498
49665
  });
49499
49666
  }
49667
+ activeOrgId() {
49668
+ const u = this.auth.user();
49669
+ return u?.activeOrgId ?? u?.activeOrg ?? '';
49670
+ }
49500
49671
  refresh() {
49501
49672
  this.loading.set(true);
49502
49673
  this.apiKeys.list().subscribe({
@@ -49509,6 +49680,7 @@ class ApiKeysModalComponent {
49509
49680
  }
49510
49681
  openCreate() {
49511
49682
  this.errorMsg.set('');
49683
+ this.selectedPerms.set([]);
49512
49684
  this.showCreate.set(true);
49513
49685
  }
49514
49686
  onCreateSubmit(event) {
@@ -49516,14 +49688,7 @@ class ApiKeysModalComponent {
49516
49688
  return;
49517
49689
  this.errorMsg.set('');
49518
49690
  const name = String(event.fields['name'] ?? '').trim();
49519
- // MULTI_SELECT puede devolver array o csv según el render — normalizamos.
49520
- const rawPerms = event.fields['permissions'];
49521
- const permissions = Array.isArray(rawPerms)
49522
- ? rawPerms
49523
- : String(rawPerms ?? '')
49524
- .split(',')
49525
- .map(s => s.trim())
49526
- .filter(Boolean);
49691
+ const permissions = this.selectedPerms();
49527
49692
  const expiresRaw = Number(event.fields['expiresInDays']);
49528
49693
  if (!name) {
49529
49694
  this.errorMsg.set(this.t('noName'));
@@ -49643,9 +49808,16 @@ class ApiKeysModalComponent {
49643
49808
  <p class="apikey-error">{{ errorMsg() }}</p>
49644
49809
  }
49645
49810
 
49646
- <!-- Crear — val-form (consistencia con el resto de modales del factory). -->
49811
+ <!-- Crear — val-form (nombre + expiración) + selector de permisos. -->
49647
49812
  @if (showCreate()) {
49648
49813
  <div class="apikey-create">
49814
+ <p class="apikey-create__label">{{ t('permissionsLabel') }} ({{ selectedPerms().length }})</p>
49815
+ <val-permission-selector
49816
+ [permissions]="catalogPermissions()"
49817
+ [actionLabels]="actionLabels()"
49818
+ [selected]="selectedPerms()"
49819
+ (selectedChange)="selectedPerms.set($event)"
49820
+ />
49649
49821
  <val-form [props]="createForm()" (onSubmit)="onCreateSubmit($event)" />
49650
49822
  <ion-button expand="block" fill="clear" color="medium" (click)="showCreate.set(false)">
49651
49823
  {{ t('cancel') }}
@@ -49661,7 +49833,7 @@ class ApiKeysModalComponent {
49661
49833
  }
49662
49834
  }
49663
49835
  </ion-content>
49664
- `, 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: "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: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { kind: "component", type: FormComponent, selector: "val-form", inputs: ["props"], outputs: ["onSubmit", "onInvalid", "onSelectChange"] }, { kind: "component", type: DisplayComponent, selector: "val-display", inputs: ["props"] }, { kind: "component", type: TextComponent, selector: "val-text", inputs: ["props"] }, { kind: "component", type: TitleComponent, selector: "val-title", inputs: ["props"] }] }); }
49836
+ `, 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: "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: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { kind: "component", type: FormComponent, selector: "val-form", inputs: ["props"], outputs: ["onSubmit", "onInvalid", "onSelectChange"] }, { kind: "component", type: DisplayComponent, selector: "val-display", inputs: ["props"] }, { kind: "component", type: TextComponent, selector: "val-text", inputs: ["props"] }, { kind: "component", type: TitleComponent, selector: "val-title", inputs: ["props"] }, { kind: "component", type: PermissionSelectorComponent, selector: "val-permission-selector", inputs: ["permissions", "actionLabels", "locale", "selected"], outputs: ["selectedChange"] }] }); }
49665
49837
  }
49666
49838
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ApiKeysModalComponent, decorators: [{
49667
49839
  type: Component,
@@ -49676,6 +49848,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
49676
49848
  DisplayComponent,
49677
49849
  TextComponent,
49678
49850
  TitleComponent,
49851
+ PermissionSelectorComponent,
49679
49852
  ], template: `
49680
49853
  <ion-header>
49681
49854
  <ion-toolbar>
@@ -49738,9 +49911,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
49738
49911
  <p class="apikey-error">{{ errorMsg() }}</p>
49739
49912
  }
49740
49913
 
49741
- <!-- Crear — val-form (consistencia con el resto de modales del factory). -->
49914
+ <!-- Crear — val-form (nombre + expiración) + selector de permisos. -->
49742
49915
  @if (showCreate()) {
49743
49916
  <div class="apikey-create">
49917
+ <p class="apikey-create__label">{{ t('permissionsLabel') }} ({{ selectedPerms().length }})</p>
49918
+ <val-permission-selector
49919
+ [permissions]="catalogPermissions()"
49920
+ [actionLabels]="actionLabels()"
49921
+ [selected]="selectedPerms()"
49922
+ (selectedChange)="selectedPerms.set($event)"
49923
+ />
49744
49924
  <val-form [props]="createForm()" (onSubmit)="onCreateSubmit($event)" />
49745
49925
  <ion-button expand="block" fill="clear" color="medium" (click)="showCreate.set(false)">
49746
49926
  {{ t('cancel') }}
@@ -49823,7 +50003,11 @@ const ORGANIZATION_VIEW_I18N = {
49823
50003
  apiKeysSectionTitle: 'Integraciones (API Keys)',
49824
50004
  apiKeysSectionHint: 'También puedes operar tu organización vía API: crea una Client API Key org-scopeada para integraciones máquina-a-máquina (por ejemplo, dar de alta miembros en masa desde tu propio sistema).',
49825
50005
  apiKeysMoreInfo: 'Más información',
49826
- orgsMoreInfo: 'Más información',
50006
+ apiKeysInfoTitle: 'Integraciones por API',
50007
+ apiKeysInfoSubtitle: 'Opera tu organización máquina-a-máquina',
50008
+ apiKeysInfoContent: 'Una API Key te permite operar tu organización desde tus propios sistemas, sin entrar a la app: dar de alta usuarios, asignar roles, leer datos y automatizar flujos.\n\nCada key es org-scopeada y lleva solo los permisos que le asignes — concede el mínimo necesario. Puedes revocarla en cualquier momento.\n\nEl secreto se muestra una sola vez al crearla: guárdalo en un lugar seguro. Si lo pierdes, genera una nueva key y revoca la anterior.',
50009
+ apiKeysInfoClose: 'Entendido',
50010
+ orgsMoreInfo: 'Aprender más sobre organizaciones',
49827
50011
  orgsInfoTitle: '¿Qué son las organizaciones?',
49828
50012
  orgsInfoSubtitle: 'Todo lo que necesitas saber',
49829
50013
  orgsInfoClose: 'Cerrar',
@@ -49902,7 +50086,11 @@ const ORGANIZATION_VIEW_I18N = {
49902
50086
  apiKeysSectionTitle: 'Integrations (API Keys)',
49903
50087
  apiKeysSectionHint: 'You can also operate your organization via API: create an org-scoped Client API Key for machine-to-machine integrations (for example, bulk-provisioning members from your own system).',
49904
50088
  apiKeysMoreInfo: 'More information',
49905
- orgsMoreInfo: 'More information',
50089
+ apiKeysInfoTitle: 'API integrations',
50090
+ apiKeysInfoSubtitle: 'Operate your organization machine-to-machine',
50091
+ apiKeysInfoContent: 'An API Key lets you operate your organization from your own systems, without opening the app: provision users, assign roles, read data, and automate flows.\n\nEach key is org-scoped and carries only the permissions you assign — grant the minimum needed. You can revoke it at any time.\n\nThe secret is shown only once at creation: store it somewhere safe. If you lose it, create a new key and revoke the old one.',
50092
+ apiKeysInfoClose: 'Got it',
50093
+ orgsMoreInfo: 'Learn more about organizations',
49906
50094
  orgsInfoTitle: 'What are organizations?',
49907
50095
  orgsInfoSubtitle: 'Everything you need to know',
49908
50096
  orgsInfoClose: 'Close',
@@ -50291,6 +50479,31 @@ class OrganizationViewComponent {
50291
50479
  backdropDismiss: true,
50292
50480
  });
50293
50481
  }
50482
+ // "Más información" de la sección Integraciones — abre un sheet EXPLICATIVO
50483
+ // (article-style, sin las feature-cards de org), NO el modal de gestión de API
50484
+ // keys. La gestión se abre con el botón "Gestionar" (onOpenApiKeys).
50485
+ onApiKeysInfo() {
50486
+ void this.modalService.open({
50487
+ component: OrgInfoSheetComponent,
50488
+ componentProps: {
50489
+ showFeatures: false,
50490
+ title: this.tt('apiKeysInfoTitle'),
50491
+ subtitle: this.tt('apiKeysInfoSubtitle'),
50492
+ body: this.tt('apiKeysInfoContent'),
50493
+ closeLabel: this.tt('apiKeysInfoClose'),
50494
+ closeProps: {
50495
+ text: this.tt('apiKeysInfoClose'),
50496
+ fill: 'outline',
50497
+ color: 'dark',
50498
+ size: 'default',
50499
+ expand: 'block',
50500
+ type: 'button',
50501
+ },
50502
+ },
50503
+ breakpoints: { breakpoints: [0, 0.85], initialBreakpoint: 0.85, showHandle: true },
50504
+ backdropDismiss: true,
50505
+ });
50506
+ }
50294
50507
  onViewPermissions() {
50295
50508
  this.nav.navigateByUrl(this.resolvedConfig().viewPermissionsRoute);
50296
50509
  }
@@ -50454,9 +50667,22 @@ class OrganizationViewComponent {
50454
50667
  admin: 'roleAdmin',
50455
50668
  super_admin: 'roleSuperAdmin',
50456
50669
  };
50457
- const key = keyMap[name] ?? name;
50458
- const label = this.tt(key);
50459
- return label && !label.startsWith('[') ? label : name;
50670
+ // 1) Rol genérico conocido → label i18n. 2) displayName/descripción del backend.
50671
+ // 3) Fallback: humanizar el nombre crudo (ej. "bingo_admin" → "Bingo Admin")
50672
+ // para no mostrar el código técnico a app-specific roles.
50673
+ const mapped = keyMap[name];
50674
+ if (mapped) {
50675
+ const label = this.tt(mapped);
50676
+ if (label && !label.startsWith('['))
50677
+ return label;
50678
+ }
50679
+ if (found?.description)
50680
+ return found.description;
50681
+ return name
50682
+ .split(/[_-]/)
50683
+ .filter(Boolean)
50684
+ .map(w => w.charAt(0).toUpperCase() + w.slice(1))
50685
+ .join(' ');
50460
50686
  }
50461
50687
  permissionLabel(perm) {
50462
50688
  if (perm === '*:*')
@@ -50517,6 +50743,11 @@ class OrganizationViewComponent {
50517
50743
  content: pageDescription(),
50518
50744
  }"
50519
50745
  />
50746
+ <!-- "Aprender más sobre organizaciones" — bajo el título+subtítulo
50747
+ principal de la vista (abre el explainer org-info-sheet). -->
50748
+ <button class="org-more-info-link" type="button" (click)="onOrgInfo()">
50749
+ {{ tt('orgsMoreInfo') }}
50750
+ </button>
50520
50751
  </header>
50521
50752
 
50522
50753
  @if (resolvedConfig().showInfo) {
@@ -50536,12 +50767,6 @@ class OrganizationViewComponent {
50536
50767
  }
50537
50768
  </div>
50538
50769
 
50539
- <!-- "Más información" sobre organizaciones — disponible siempre (todo
50540
- user tiene org por defecto y no ve el empty-state de account). -->
50541
- <button class="org-more-info-link" type="button" (click)="onOrgInfo()">
50542
- {{ tt('orgsMoreInfo') }}
50543
- </button>
50544
-
50545
50770
  <div class="section-body">
50546
50771
  @if (loading()) {
50547
50772
  <val-skeleton-layout [props]="{ preset: 'detail', rows: 4 }" />
@@ -50694,7 +50919,7 @@ class OrganizationViewComponent {
50694
50919
  <div class="invite-cta__text">
50695
50920
  <val-title [props]="{ size: 'medium', color: 'dark', bold: true, content: tt('apiKeysSectionTitle') }" />
50696
50921
  <val-text [props]="{ size: 'medium', color: 'dark', bold: false, content: tt('apiKeysSectionHint') }" />
50697
- <button class="org-more-info-link" type="button" (click)="onOpenApiKeys()">
50922
+ <button class="org-more-info-link" type="button" (click)="onApiKeysInfo()">
50698
50923
  {{ tt('apiKeysMoreInfo') }}
50699
50924
  </button>
50700
50925
  </div>
@@ -50821,6 +51046,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
50821
51046
  content: pageDescription(),
50822
51047
  }"
50823
51048
  />
51049
+ <!-- "Aprender más sobre organizaciones" — bajo el título+subtítulo
51050
+ principal de la vista (abre el explainer org-info-sheet). -->
51051
+ <button class="org-more-info-link" type="button" (click)="onOrgInfo()">
51052
+ {{ tt('orgsMoreInfo') }}
51053
+ </button>
50824
51054
  </header>
50825
51055
 
50826
51056
  @if (resolvedConfig().showInfo) {
@@ -50840,12 +51070,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
50840
51070
  }
50841
51071
  </div>
50842
51072
 
50843
- <!-- "Más información" sobre organizaciones — disponible siempre (todo
50844
- user tiene org por defecto y no ve el empty-state de account). -->
50845
- <button class="org-more-info-link" type="button" (click)="onOrgInfo()">
50846
- {{ tt('orgsMoreInfo') }}
50847
- </button>
50848
-
50849
51073
  <div class="section-body">
50850
51074
  @if (loading()) {
50851
51075
  <val-skeleton-layout [props]="{ preset: 'detail', rows: 4 }" />
@@ -50998,7 +51222,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
50998
51222
  <div class="invite-cta__text">
50999
51223
  <val-title [props]="{ size: 'medium', color: 'dark', bold: true, content: tt('apiKeysSectionTitle') }" />
51000
51224
  <val-text [props]="{ size: 'medium', color: 'dark', bold: false, content: tt('apiKeysSectionHint') }" />
51001
- <button class="org-more-info-link" type="button" (click)="onOpenApiKeys()">
51225
+ <button class="org-more-info-link" type="button" (click)="onApiKeysInfo()">
51002
51226
  {{ tt('apiKeysMoreInfo') }}
51003
51227
  </button>
51004
51228
  </div>
@@ -51877,35 +52101,6 @@ function provideValtechNotificationPreferencesRoutes(opts) {
51877
52101
  ];
51878
52102
  }
51879
52103
 
51880
- /**
51881
- * Consume el catálogo de permisos del backend (`GET /org/{orgId}/permissions-catalog`).
51882
- * El backend es la fuente de verdad de qué permisos existen y de su alcance
51883
- * (globales de plataforma vs. propios de una app). La respuesta incluye el
51884
- * `appId` del request para que la vista distinga la app actual.
51885
- *
51886
- * Promovido desde `showcase` a la lib bajo el proceso de ADR-021. Usa
51887
- * `VALTECH_AUTH_CONFIG.apiUrl` (como `OrgService`) en vez del `environment` de la
51888
- * app — la lib no importa el environment del consumer.
51889
- */
51890
- class PermissionCatalogService {
51891
- constructor(config, http) {
51892
- this.config = config;
51893
- this.http = http;
51894
- }
51895
- getCatalog(orgId) {
51896
- return this.http.get(`${this.config.apiUrl}/org/${orgId}/permissions-catalog`);
51897
- }
51898
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: PermissionCatalogService, deps: [{ token: VALTECH_AUTH_CONFIG }, { token: i1$3.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable }); }
51899
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: PermissionCatalogService, providedIn: 'root' }); }
51900
- }
51901
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: PermissionCatalogService, decorators: [{
51902
- type: Injectable,
51903
- args: [{ providedIn: 'root' }]
51904
- }], ctorParameters: () => [{ type: undefined, decorators: [{
51905
- type: Inject,
51906
- args: [VALTECH_AUTH_CONFIG]
51907
- }] }, { type: i1$3.HttpClient }] });
51908
-
51909
52104
  /**
51910
52105
  * Defaults i18n (es/en) embebidos en `val-permissions-view`. Auto-registrados en
51911
52106
  * el constructor del componente si el consumer no proveyó el namespace
@@ -53913,6 +54108,11 @@ addIcons({
53913
54108
  const DEFAULT_NAMESPACE = 'Settings.About';
53914
54109
  const DEFAULT_BRAND_NAME = 'Valtech';
53915
54110
  const DEFAULT_LOGO_SRC = 'assets/images/main-icon.png';
54111
+ // Por default el HERO usa la CSS var de marca del factory (`--main-logo-mini`),
54112
+ // que cada app define en su `theme/variables.scss` (theme-aware). Si la app no
54113
+ // la define, el `var(...)` cae al `logoSrc` (ver template). El consumer puede
54114
+ // pasar `brand.logoVar: ''` para forzar el `<img src=logoSrc>` clásico.
54115
+ const DEFAULT_LOGO_VAR = '--main-logo-mini';
53916
54116
  const DEFAULT_RATING_ENTITY_ID = 'experience';
53917
54117
  /**
53918
54118
  * `val-about-view` — vista About full-feature autocontenida (organism).
@@ -53961,7 +54161,7 @@ class AboutViewComponent {
53961
54161
  brand: {
53962
54162
  name: merged.brand?.name ?? DEFAULT_BRAND_NAME,
53963
54163
  logoSrc: merged.brand?.logoSrc ?? DEFAULT_LOGO_SRC,
53964
- logoVar: merged.brand?.logoVar ?? '',
54164
+ logoVar: merged.brand?.logoVar ?? DEFAULT_LOGO_VAR,
53965
54165
  },
53966
54166
  version: merged.version ?? '',
53967
54167
  socialLinks: merged.socialLinks ?? [],
@@ -54103,7 +54303,7 @@ class AboutViewComponent {
54103
54303
  class="about-hero__logo about-hero__logo--var"
54104
54304
  role="img"
54105
54305
  [attr.aria-label]="brandName()"
54106
- [style.background-image]="'var(' + logoVar() + ')'"
54306
+ [style.background-image]="'var(' + logoVar() + ', url(' + logoSrc() + '))'"
54107
54307
  ></div>
54108
54308
  } @else {
54109
54309
  <img class="about-hero__logo" [src]="logoSrc()" [alt]="brandName()" />
@@ -54225,7 +54425,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
54225
54425
  class="about-hero__logo about-hero__logo--var"
54226
54426
  role="img"
54227
54427
  [attr.aria-label]="brandName()"
54228
- [style.background-image]="'var(' + logoVar() + ')'"
54428
+ [style.background-image]="'var(' + logoVar() + ', url(' + logoSrc() + '))'"
54229
54429
  ></div>
54230
54430
  } @else {
54231
54431
  <img class="about-hero__logo" [src]="logoSrc()" [alt]="brandName()" />
@@ -64373,5 +64573,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
64373
64573
  * Generated bundle index. Do not edit.
64374
64574
  */
64375
64575
 
64376
- 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, AboutViewComponent, 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_SETTINGS_MENU_LINKS, 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, provideValtechAboutRoutes, 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 };
64576
+ 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, AboutViewComponent, 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, PermissionSelectorComponent, 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_SETTINGS_MENU_LINKS, 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, provideValtechAboutRoutes, 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 };
64377
64577
  //# sourceMappingURL=valtech-components.mjs.map