valtech-components 2.0.984 → 2.0.986

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 (35) hide show
  1. package/esm2022/lib/components/molecules/date-input/date-input.component.mjs +97 -14
  2. package/esm2022/lib/components/organisms/account-view/account-view.component.mjs +732 -0
  3. package/esm2022/lib/components/organisms/account-view/account-view.i18n.mjs +96 -0
  4. package/esm2022/lib/components/organisms/account-view/account.routes.mjs +29 -0
  5. package/esm2022/lib/components/organisms/account-view/types.mjs +2 -0
  6. package/esm2022/lib/components/organisms/bottom-nav/bottom-nav.component.mjs +3 -3
  7. package/esm2022/lib/components/organisms/create-org-modal/create-org-modal.component.mjs +240 -0
  8. package/esm2022/lib/components/organisms/create-org-modal/create-org-modal.i18n.mjs +46 -0
  9. package/esm2022/lib/components/organisms/delete-account-modal/delete-account-modal.component.mjs +272 -0
  10. package/esm2022/lib/components/organisms/delete-account-modal/delete-account-modal.i18n.mjs +40 -0
  11. package/esm2022/lib/components/organisms/org-info-sheet/org-info-sheet.component.mjs +205 -0
  12. package/esm2022/lib/components/organisms/org-info-sheet/org-info-sheet.i18n.mjs +35 -0
  13. package/esm2022/lib/components/organisms/switch-org-modal/switch-org-modal.component.mjs +280 -0
  14. package/esm2022/lib/components/organisms/switch-org-modal/switch-org-modal.i18n.mjs +26 -0
  15. package/esm2022/lib/services/font-size/font-size.service.mjs +3 -1
  16. package/esm2022/lib/version.mjs +2 -2
  17. package/esm2022/public-api.mjs +8 -1
  18. package/fesm2022/valtech-components.mjs +2279 -279
  19. package/fesm2022/valtech-components.mjs.map +1 -1
  20. package/lib/components/molecules/date-input/date-input.component.d.ts +9 -0
  21. package/lib/components/organisms/account-view/account-view.component.d.ts +88 -0
  22. package/lib/components/organisms/account-view/account-view.i18n.d.ts +7 -0
  23. package/lib/components/organisms/account-view/account.routes.d.ts +24 -0
  24. package/lib/components/organisms/account-view/types.d.ts +44 -0
  25. package/lib/components/organisms/create-org-modal/create-org-modal.component.d.ts +45 -0
  26. package/lib/components/organisms/create-org-modal/create-org-modal.i18n.d.ts +7 -0
  27. package/lib/components/organisms/delete-account-modal/delete-account-modal.component.d.ts +50 -0
  28. package/lib/components/organisms/delete-account-modal/delete-account-modal.i18n.d.ts +7 -0
  29. package/lib/components/organisms/org-info-sheet/org-info-sheet.component.d.ts +44 -0
  30. package/lib/components/organisms/org-info-sheet/org-info-sheet.i18n.d.ts +12 -0
  31. package/lib/components/organisms/switch-org-modal/switch-org-modal.component.d.ts +47 -0
  32. package/lib/components/organisms/switch-org-modal/switch-org-modal.i18n.d.ts +7 -0
  33. package/lib/version.d.ts +1 -1
  34. package/package.json +1 -1
  35. package/public-api.d.ts +7 -0
@@ -7,7 +7,7 @@ import { provideHttpClient, withInterceptors, HttpClient, HttpErrorResponse } fr
7
7
  import * as i1$1 from '@angular/fire/firestore';
8
8
  import { provideFirestore, getFirestore, connectFirestoreEmulator, enableIndexedDbPersistence, doc, getDoc, collection, query as query$1, getDocs, getCountFromServer, limit, docData, collectionData, serverTimestamp, addDoc, setDoc, updateDoc, deleteDoc, writeBatch, arrayUnion, arrayRemove, increment, where, orderBy, startAfter, startAt, endBefore, endAt, Timestamp, onSnapshot } from '@angular/fire/firestore';
9
9
  import * as i1$5 from '@angular/common';
10
- import { isPlatformBrowser, CommonModule, NgStyle, NgFor, DOCUMENT, NgClass } from '@angular/common';
10
+ import { isPlatformBrowser, CommonModule, NgStyle, NgFor, DOCUMENT, NgClass, TitleCasePipe } from '@angular/common';
11
11
  import { provideAnalytics, getAnalytics, Analytics, logEvent, setUserId, setUserProperties } from '@angular/fire/analytics';
12
12
  import { provideFirebaseApp, initializeApp } from '@angular/fire/app';
13
13
  import * as i1 from '@angular/fire/auth';
@@ -23,9 +23,9 @@ import { IonicModule, ToastController, NavController } from '@ionic/angular';
23
23
  import { isSupported, getMessaging as getMessaging$1 } from 'firebase/messaging';
24
24
  import { getApps, getApp, initializeApp as initializeApp$1 } from 'firebase/app';
25
25
  import * as i2$1 from '@ionic/angular/standalone';
26
- import { AlertController, IonAvatar, IonCard, IonIcon, IonButton, IonSpinner, IonText, IonModal, IonHeader, IonToolbar, IonContent, IonButtons, IonTitle, IonProgressBar, IonSkeletonText, IonFab, IonFabButton, IonFabList, IonLabel, IonCardContent, IonCardHeader, IonCardTitle, IonCardSubtitle, IonCheckbox, IonTextarea, IonDatetime, IonDatetimeButton, IonInput, IonSelect, IonSelectOption, IonPopover, IonList, IonItem, IonRadioGroup, IonRadio, IonRange, IonSearchbar, IonSegment, IonSegmentButton, IonToggle, IonRippleEffect, IonAccordion, IonAccordionGroup, IonTabBar, IonTabButton, IonBadge, IonBreadcrumb, IonBreadcrumbs, IonChip, IonNote, ToastController as ToastController$1, IonCol, IonRow, IonRefresher, IonRefresherContent, IonMenuButton, IonFooter, IonListHeader, IonInfiniteScroll, IonInfiniteScrollContent, IonGrid, MenuController, IonMenu, IonMenuToggle, IonSplitPane } from '@ionic/angular/standalone';
26
+ import { AlertController, IonAvatar, IonCard, IonIcon, IonButton, IonSpinner, IonText, IonModal, IonHeader, IonToolbar, IonContent, IonButtons, IonTitle, IonProgressBar, IonSkeletonText, IonFab, IonFabButton, IonFabList, IonLabel, IonCardContent, IonCardHeader, IonCardTitle, IonCardSubtitle, IonCheckbox, IonTextarea, IonDatetime, IonInput, IonSelect, IonSelectOption, IonPopover, IonList, IonItem, IonRadioGroup, IonRadio, IonRange, IonSearchbar, IonSegment, IonSegmentButton, IonToggle, IonRippleEffect, IonAccordion, IonAccordionGroup, IonTabBar, IonTabButton, IonBadge, IonBreadcrumb, IonBreadcrumbs, IonChip, IonNote, IonDatetimeButton, ToastController as ToastController$1, IonCol, IonRow, IonRefresher, IonRefresherContent, IonMenuButton, IonFooter, IonListHeader, IonInfiniteScroll, IonInfiniteScrollContent, IonGrid, MenuController, IonMenu, IonMenuToggle, IonSplitPane } from '@ionic/angular/standalone';
27
27
  import { addIcons } from 'ionicons';
28
- import { addOutline, addCircleOutline, alertOutline, alertCircleOutline, arrowBackOutline, arrowForwardOutline, arrowDownOutline, settings, settingsOutline, checkmarkCircleOutline, ellipsisHorizontalOutline, notifications, notificationsOutline, openOutline, closeOutline, chatbubblesOutline, shareOutline, heart, heartOutline, home, homeOutline, eyeOffOutline, eyeOutline, scanOutline, chevronDownOutline, chevronForwardOutline, checkmarkOutline, clipboardOutline, copyOutline, filterOutline, locationOutline, calendarOutline, businessOutline, logoTwitter, logoInstagram, logoLinkedin, logoYoutube, logoTiktok, logoFacebook, logoGoogle, createOutline, trashOutline, playOutline, peopleOutline, phonePortraitOutline, refreshOutline, documentTextOutline, lockClosedOutline, informationCircleOutline, logoNpm, removeOutline, optionsOutline, personOutline, shieldCheckmarkOutline, keyOutline, desktopOutline, logOutOutline, cloudDownloadOutline, cloudOutline, gitBranchOutline, layersOutline, serverOutline, warningOutline, bulbOutline, lockOpenOutline, diamondOutline, add, close, share, create, trash, star, camera, mic, send, downloadOutline, chevronDown, language, globeOutline, checkmark, list, grid, apps, menu, search, person, helpCircle, informationCircle, documentText, mail, calendar, folder, chevronForward, ellipsisHorizontal, chevronBack, playBack, playForward, ellipse, starOutline, starHalf, heartHalf, checkmarkCircle, timeOutline, flag, trendingUp, trendingDown, remove, analytics, people, cash, cart, eye, chatbubbleOutline, thumbsUpOutline, thumbsUp, happyOutline, happy, sadOutline, sad, chevronUp, pin, pencil, callOutline, logoWhatsapp, paperPlaneOutline, mailOutline, chevronDownCircleOutline, closeCircle, alertCircle, logoApple, logoMicrosoft, linkOutline, unlinkOutline, cloudOfflineOutline, documentOutline, attachOutline, cameraOutline, closeCircleOutline, imageOutline, chevronBackOutline, sendOutline, chatbubbleEllipsesOutline, swapVerticalOutline, chevronUpOutline, searchOutline, cartOutline, chatbubble, compass, compassOutline, gridOutline, listOutline, folderOutline, documents, documentsOutline, statsChart, statsChartOutline, laptopOutline, menuOutline } from 'ionicons/icons';
28
+ import { addOutline, addCircleOutline, alertOutline, alertCircleOutline, arrowBackOutline, arrowForwardOutline, arrowDownOutline, settings, settingsOutline, checkmarkCircleOutline, ellipsisHorizontalOutline, notifications, notificationsOutline, openOutline, closeOutline, chatbubblesOutline, shareOutline, heart, heartOutline, home, homeOutline, eyeOffOutline, eyeOutline, scanOutline, chevronDownOutline, chevronForwardOutline, checkmarkOutline, clipboardOutline, copyOutline, filterOutline, locationOutline, calendarOutline, businessOutline, logoTwitter, logoInstagram, logoLinkedin, logoYoutube, logoTiktok, logoFacebook, logoGoogle, createOutline, trashOutline, playOutline, peopleOutline, phonePortraitOutline, refreshOutline, documentTextOutline, lockClosedOutline, informationCircleOutline, logoNpm, removeOutline, optionsOutline, personOutline, shieldCheckmarkOutline, keyOutline, desktopOutline, logOutOutline, cloudDownloadOutline, cloudOutline, gitBranchOutline, layersOutline, serverOutline, warningOutline, bulbOutline, lockOpenOutline, diamondOutline, add, close, share, create, trash, star, camera, mic, send, downloadOutline, chevronDown, language, globeOutline, checkmark, list, grid, apps, menu, search, person, helpCircle, informationCircle, documentText, mail, calendar, folder, chevronForward, ellipsisHorizontal, chevronBack, playBack, playForward, ellipse, starOutline, starHalf, heartHalf, checkmarkCircle, timeOutline, flag, trendingUp, trendingDown, remove, analytics, people, cash, cart, eye, chatbubbleOutline, thumbsUpOutline, thumbsUp, happyOutline, happy, sadOutline, sad, chevronUp, pin, pencil, callOutline, logoWhatsapp, paperPlaneOutline, mailOutline, chevronDownCircleOutline, closeCircle, alertCircle, logoApple, logoMicrosoft, linkOutline, unlinkOutline, cloudOfflineOutline, documentOutline, attachOutline, cameraOutline, closeCircleOutline, imageOutline, chevronBackOutline, sendOutline, chatbubbleEllipsesOutline, swapVerticalOutline, chevronUpOutline, searchOutline, cartOutline, chatbubble, compass, compassOutline, gridOutline, listOutline, folderOutline, documents, documentsOutline, statsChart, statsChartOutline, laptopOutline, swapHorizontalOutline, menuOutline } from 'ionicons/icons';
29
29
  import { Browser } from '@capacitor/browser';
30
30
  import * as i1$6 from '@angular/platform-browser';
31
31
  import { DomSanitizer, Meta, Title } from '@angular/platform-browser';
@@ -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.984';
57
+ const VERSION = '2.0.986';
58
58
 
59
59
  // Control de estado de refresco (singleton a nivel de módulo)
60
60
  let isRefreshing = false;
@@ -15796,11 +15796,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
15796
15796
  type: Input
15797
15797
  }] } });
15798
15798
 
15799
+ const DATE_INPUT_I18N = {
15800
+ es: { selectDate: 'Seleccionar fecha', selectTime: 'Seleccionar hora' },
15801
+ en: { selectDate: 'Select date', selectTime: 'Select time' },
15802
+ };
15799
15803
  /**
15800
15804
  * val-date-input
15801
15805
  *
15802
15806
  * A date picker input integrated with Angular forms, using Ionic's datetime component.
15803
15807
  *
15808
+ * Cuando el control no tiene valor, muestra un botón con el texto `placeholder`
15809
+ * (o un default i18n "Seleccionar fecha") en vez de renderizar en blanco —
15810
+ * `ion-datetime-button` no soporta placeholder, por eso usamos un trigger propio.
15811
+ *
15804
15812
  * @example
15805
15813
  * <val-date-input [props]="{ control: myControl, hint: 'Select a date' }"></val-date-input>
15806
15814
  *
@@ -15821,6 +15829,10 @@ class DateInputComponent {
15821
15829
  * Resolved props after merging preset + explicit props.
15822
15830
  */
15823
15831
  this.resolvedProps = {};
15832
+ // Auto-registro de defaults i18n (anti key-faltante → anti storm de CD).
15833
+ if (!this.i18n.hasNamespace('DateInput')) {
15834
+ this.i18n.registerContent('DateInput', DATE_INPUT_I18N);
15835
+ }
15824
15836
  }
15825
15837
  /** Done button text - from props or i18n default */
15826
15838
  get doneText() {
@@ -15830,6 +15842,52 @@ class DateInputComponent {
15830
15842
  get cancelText() {
15831
15843
  return this.resolvedProps?.cancelText || this.i18n.t('cancel');
15832
15844
  }
15845
+ /** Presentación del datetime (default: date). */
15846
+ get presentation() {
15847
+ return this.resolvedProps?.presentation || 'date';
15848
+ }
15849
+ /** Texto del trigger cuando NO hay valor (placeholder explícito o default i18n). */
15850
+ get placeholderText() {
15851
+ if (this.resolvedProps?.placeholder)
15852
+ return this.resolvedProps.placeholder;
15853
+ this.i18n.lang(); // reactividad ante cambio de idioma
15854
+ return this.presentation === 'time'
15855
+ ? this.i18n.t('selectTime', 'DateInput')
15856
+ : this.i18n.t('selectDate', 'DateInput');
15857
+ }
15858
+ /** Id del trigger/datetime — estable; se garantiza no vacío en resolveProps. */
15859
+ get triggerId() {
15860
+ return this.resolvedProps?.token || '';
15861
+ }
15862
+ /** Valor formateado del control para mostrar en el trigger ('' si vacío). */
15863
+ get displayValue() {
15864
+ const raw = this.resolvedProps?.control?.value;
15865
+ if (!raw)
15866
+ return '';
15867
+ const d = new Date(raw);
15868
+ if (isNaN(d.getTime()))
15869
+ return String(raw);
15870
+ this.i18n.lang(); // reactividad
15871
+ const locale = 'es-ES';
15872
+ try {
15873
+ switch (this.presentation) {
15874
+ case 'time':
15875
+ return new Intl.DateTimeFormat(locale, { timeStyle: 'short' }).format(d);
15876
+ case 'month':
15877
+ case 'month-year':
15878
+ return new Intl.DateTimeFormat(locale, { month: 'long', year: 'numeric' }).format(d);
15879
+ case 'year':
15880
+ return new Intl.DateTimeFormat(locale, { year: 'numeric' }).format(d);
15881
+ case 'date-time':
15882
+ return new Intl.DateTimeFormat(locale, { dateStyle: 'medium', timeStyle: 'short' }).format(d);
15883
+ default:
15884
+ return new Intl.DateTimeFormat(locale, { dateStyle: 'medium' }).format(d);
15885
+ }
15886
+ }
15887
+ catch {
15888
+ return String(raw);
15889
+ }
15890
+ }
15833
15891
  ngOnInit() {
15834
15892
  this.resolveProps();
15835
15893
  this.applyDefaultValue();
@@ -15845,13 +15903,16 @@ class DateInputComponent {
15845
15903
  * Explicit props take precedence over preset values.
15846
15904
  */
15847
15905
  resolveProps() {
15848
- const presetProps = this.preset
15849
- ? this.presets.get('dateInput', this.preset)
15850
- : {};
15906
+ const presetProps = this.preset ? this.presets.get('dateInput', this.preset) : {};
15851
15907
  this.resolvedProps = {
15852
15908
  ...presetProps,
15853
15909
  ...this.props,
15854
15910
  };
15911
+ // Garantizar un token (id) no vacío: lo necesitan [trigger] del modal y el
15912
+ // [id] del ion-datetime para vincularse. Si el consumer no lo pasó, generamos uno.
15913
+ if (!this.resolvedProps.token) {
15914
+ this.resolvedProps.token = `val-date-${Math.random().toString(36).slice(2, 10)}`;
15915
+ }
15855
15916
  }
15856
15917
  applyDefaultValue() {
15857
15918
  if (!this.resolvedProps.control)
@@ -15879,14 +15940,25 @@ class DateInputComponent {
15879
15940
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DateInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
15880
15941
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: DateInputComponent, isStandalone: true, selector: "val-date-input", inputs: { preset: "preset", props: "props" }, usesOnChanges: true, ngImport: i0, template: `
15881
15942
  <div class="button-container">
15882
- <ion-datetime-button class="action" [datetime]="resolvedProps.token"></ion-datetime-button>
15943
+ <!-- Trigger propio (reemplaza ion-datetime-button, que renderiza en blanco
15944
+ cuando el control no tiene valor). Muestra el valor formateado o el
15945
+ placeholder, y abre el modal vía [trigger]. -->
15946
+ <ion-button
15947
+ [id]="triggerId"
15948
+ class="action date-trigger"
15949
+ fill="outline"
15950
+ color="medium"
15951
+ [class.is-placeholder]="!displayValue"
15952
+ >
15953
+ {{ displayValue || placeholderText }}
15954
+ </ion-button>
15883
15955
  </div>
15884
- <ion-modal [keepContentsMounted]="true">
15956
+ <ion-modal [trigger]="triggerId" [keepContentsMounted]="true">
15885
15957
  <ng-template>
15886
15958
  <ion-datetime
15887
15959
  [formControl]="resolvedProps.control"
15888
15960
  [id]="resolvedProps.token"
15889
- presentation="date"
15961
+ [presentation]="presentation"
15890
15962
  locale="es-ES"
15891
15963
  [firstDayOfWeek]="1"
15892
15964
  [showDefaultButtons]="true"
@@ -15901,20 +15973,31 @@ class DateInputComponent {
15901
15973
  </ion-datetime>
15902
15974
  </ng-template>
15903
15975
  </ion-modal>
15904
- `, isInline: true, styles: ["@charset \"UTF-8\";:root{--val-container-sm: 540px;--val-container-md: 720px;--val-container-lg: 880px;--val-container-xl: 1100px;--val-container-padding: 16px;--ion-color-primary: #7026df;--ion-color-primary-rgb: 112, 38, 223;--ion-color-primary-contrast: #ffffff;--ion-color-primary-contrast-rgb: 255, 255, 255;--ion-color-primary-shade: #6321c4;--ion-color-primary-tint: #7e3ce2;--ion-color-secondary: #e2ccff;--ion-color-secondary-rgb: 226, 204, 255;--ion-color-secondary-contrast: #000000;--ion-color-secondary-contrast-rgb: 0, 0, 0;--ion-color-secondary-shade: #c7b4e0;--ion-color-secondary-tint: #e5d1ff;--ion-color-texti: #354c69;--ion-color-texti-rgb: 53, 76, 105;--ion-color-texti-contrast: #ffffff;--ion-color-texti-contrast-rgb: 255, 255, 255;--ion-color-texti-shade: #2f435c;--ion-color-texti-tint: #495e78;--ion-color-darki: #090f1b;--ion-color-darki-rgb: 9, 15, 27;--ion-color-darki-contrast: #ffffff;--ion-color-darki-contrast-rgb: 255, 255, 255;--ion-color-darki-shade: #080d18;--ion-color-darki-tint: #222732;--ion-color-medium: #9e9e9e;--ion-color-medium-rgb: 158, 158, 158;--ion-color-medium-contrast: #000000;--ion-color-medium-contrast-rgb: 0, 0, 0;--ion-color-medium-shade: #8b8b8b;--ion-color-medium-tint: #a8a8a8;--swiper-pagination-color: var(--ion-color-primary);--swiper-navigation-color: var(--ion-color-primary);--swiper-pagination-bullet-inactive-color: var(--ion-color-medium)}body.dark,html.ion-palette-dark,body[data-theme=dark]{--ion-color-texti: #8fc1ff;--ion-color-texti-rgb: 143, 193, 255;--ion-color-texti-contrast: #000000;--ion-color-texti-contrast-rgb: 0, 0, 0;--ion-color-texti-shade: #7eaae0;--ion-color-texti-tint: #9ac7ff;--ion-color-darki: #ffffff;--ion-color-darki-rgb: 255, 255, 255;--ion-color-darki-contrast: #000000;--ion-color-darki-contrast-rgb: 0, 0, 0;--ion-color-darki-shade: #e0e0e0;--ion-color-darki-tint: #ffffff;--ion-color-primary: #8f49f8;--ion-color-primary-rgb: 143, 73, 248;--ion-color-primary-contrast: #ffffff;--ion-color-primary-contrast-rgb: 255, 255, 255;--ion-color-primary-shade: #7e40da;--ion-color-primary-tint: #9a5bf9}.ion-color-texti{--ion-color-base: var(--ion-color-texti);--ion-color-base-rgb: var(--ion-color-texti-rgb);--ion-color-contrast: var(--ion-color-texti-contrast);--ion-color-contrast-rgb: var(--ion-color-texti-contrast-rgb);--ion-color-shade: var(--ion-color-texti-shade);--ion-color-tint: var(--ion-color-texti-tint)}.ion-color-darki{--ion-color-base: var(--ion-color-darki);--ion-color-base-rgb: var(--ion-color-darki-rgb);--ion-color-contrast: var(--ion-color-darki-contrast);--ion-color-contrast-rgb: var(--ion-color-darki-contrast-rgb);--ion-color-shade: var(--ion-color-darki-shade);--ion-color-tint: var(--ion-color-darki-tint)}.section{margin-top:1rem}.input{margin:var(--val-form-field-gap, .5rem) 0}@media (min-width: 768px){.input{margin:var(--val-form-field-gap, .75rem) 0}}.submit-actions--sticky{position:sticky;bottom:0;z-index:1;background:var(--ion-background-color);border-top:1px solid var(--ion-color-step-150, rgba(0, 0, 0, .08));padding:.75rem 0 calc(.75rem + env(safe-area-inset-bottom))}.field-description{display:block;font-size:.75rem;color:var(--ion-color-dark);margin-bottom:.25rem;line-height:1.4}.button-container{display:flex;flex-direction:column;align-items:flex-start}.action{margin-top:.25rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$7.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$7.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "component", type: IonDatetime, selector: "ion-datetime", inputs: ["cancelText", "clearText", "color", "dayValues", "disabled", "doneText", "firstDayOfWeek", "formatOptions", "highlightedDates", "hourCycle", "hourValues", "isDateEnabled", "locale", "max", "min", "minuteValues", "mode", "monthValues", "multiple", "name", "preferWheel", "presentation", "readonly", "showAdjacentDays", "showClearButton", "showDefaultButtons", "showDefaultTimeLabel", "showDefaultTitle", "size", "titleSelectedDatesFormatter", "value", "yearValues"] }, { kind: "component", type: IonDatetimeButton, selector: "ion-datetime-button", inputs: ["color", "datetime", "disabled", "mode"] }, { kind: "component", type: IonModal, selector: "ion-modal" }] }); }
15976
+ `, isInline: true, styles: ["@charset \"UTF-8\";:root{--val-container-sm: 540px;--val-container-md: 720px;--val-container-lg: 880px;--val-container-xl: 1100px;--val-container-padding: 16px;--ion-color-primary: #7026df;--ion-color-primary-rgb: 112, 38, 223;--ion-color-primary-contrast: #ffffff;--ion-color-primary-contrast-rgb: 255, 255, 255;--ion-color-primary-shade: #6321c4;--ion-color-primary-tint: #7e3ce2;--ion-color-secondary: #e2ccff;--ion-color-secondary-rgb: 226, 204, 255;--ion-color-secondary-contrast: #000000;--ion-color-secondary-contrast-rgb: 0, 0, 0;--ion-color-secondary-shade: #c7b4e0;--ion-color-secondary-tint: #e5d1ff;--ion-color-texti: #354c69;--ion-color-texti-rgb: 53, 76, 105;--ion-color-texti-contrast: #ffffff;--ion-color-texti-contrast-rgb: 255, 255, 255;--ion-color-texti-shade: #2f435c;--ion-color-texti-tint: #495e78;--ion-color-darki: #090f1b;--ion-color-darki-rgb: 9, 15, 27;--ion-color-darki-contrast: #ffffff;--ion-color-darki-contrast-rgb: 255, 255, 255;--ion-color-darki-shade: #080d18;--ion-color-darki-tint: #222732;--ion-color-medium: #9e9e9e;--ion-color-medium-rgb: 158, 158, 158;--ion-color-medium-contrast: #000000;--ion-color-medium-contrast-rgb: 0, 0, 0;--ion-color-medium-shade: #8b8b8b;--ion-color-medium-tint: #a8a8a8;--swiper-pagination-color: var(--ion-color-primary);--swiper-navigation-color: var(--ion-color-primary);--swiper-pagination-bullet-inactive-color: var(--ion-color-medium)}body.dark,html.ion-palette-dark,body[data-theme=dark]{--ion-color-texti: #8fc1ff;--ion-color-texti-rgb: 143, 193, 255;--ion-color-texti-contrast: #000000;--ion-color-texti-contrast-rgb: 0, 0, 0;--ion-color-texti-shade: #7eaae0;--ion-color-texti-tint: #9ac7ff;--ion-color-darki: #ffffff;--ion-color-darki-rgb: 255, 255, 255;--ion-color-darki-contrast: #000000;--ion-color-darki-contrast-rgb: 0, 0, 0;--ion-color-darki-shade: #e0e0e0;--ion-color-darki-tint: #ffffff;--ion-color-primary: #8f49f8;--ion-color-primary-rgb: 143, 73, 248;--ion-color-primary-contrast: #ffffff;--ion-color-primary-contrast-rgb: 255, 255, 255;--ion-color-primary-shade: #7e40da;--ion-color-primary-tint: #9a5bf9}.ion-color-texti{--ion-color-base: var(--ion-color-texti);--ion-color-base-rgb: var(--ion-color-texti-rgb);--ion-color-contrast: var(--ion-color-texti-contrast);--ion-color-contrast-rgb: var(--ion-color-texti-contrast-rgb);--ion-color-shade: var(--ion-color-texti-shade);--ion-color-tint: var(--ion-color-texti-tint)}.ion-color-darki{--ion-color-base: var(--ion-color-darki);--ion-color-base-rgb: var(--ion-color-darki-rgb);--ion-color-contrast: var(--ion-color-darki-contrast);--ion-color-contrast-rgb: var(--ion-color-darki-contrast-rgb);--ion-color-shade: var(--ion-color-darki-shade);--ion-color-tint: var(--ion-color-darki-tint)}.section{margin-top:1rem}.input{margin:var(--val-form-field-gap, .5rem) 0}@media (min-width: 768px){.input{margin:var(--val-form-field-gap, .75rem) 0}}.submit-actions--sticky{position:sticky;bottom:0;z-index:1;background:var(--ion-background-color);border-top:1px solid var(--ion-color-step-150, rgba(0, 0, 0, .08));padding:.75rem 0 calc(.75rem + env(safe-area-inset-bottom))}.field-description{display:block;font-size:.75rem;color:var(--ion-color-dark);margin-bottom:.25rem;line-height:1.4}.button-container{display:flex;flex-direction:column;align-items:flex-start}.action{margin-top:.25rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$7.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$7.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { 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: IonDatetime, selector: "ion-datetime", inputs: ["cancelText", "clearText", "color", "dayValues", "disabled", "doneText", "firstDayOfWeek", "formatOptions", "highlightedDates", "hourCycle", "hourValues", "isDateEnabled", "locale", "max", "min", "minuteValues", "mode", "monthValues", "multiple", "name", "preferWheel", "presentation", "readonly", "showAdjacentDays", "showClearButton", "showDefaultButtons", "showDefaultTimeLabel", "showDefaultTitle", "size", "titleSelectedDatesFormatter", "value", "yearValues"] }, { kind: "component", type: IonModal, selector: "ion-modal" }] }); }
15905
15977
  }
15906
15978
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DateInputComponent, decorators: [{
15907
15979
  type: Component,
15908
- args: [{ selector: 'val-date-input', standalone: true, imports: [CommonModule, ReactiveFormsModule, IonDatetime, IonDatetimeButton, IonModal], template: `
15980
+ args: [{ selector: 'val-date-input', standalone: true, imports: [CommonModule, ReactiveFormsModule, IonButton, IonDatetime, IonModal], template: `
15909
15981
  <div class="button-container">
15910
- <ion-datetime-button class="action" [datetime]="resolvedProps.token"></ion-datetime-button>
15982
+ <!-- Trigger propio (reemplaza ion-datetime-button, que renderiza en blanco
15983
+ cuando el control no tiene valor). Muestra el valor formateado o el
15984
+ placeholder, y abre el modal vía [trigger]. -->
15985
+ <ion-button
15986
+ [id]="triggerId"
15987
+ class="action date-trigger"
15988
+ fill="outline"
15989
+ color="medium"
15990
+ [class.is-placeholder]="!displayValue"
15991
+ >
15992
+ {{ displayValue || placeholderText }}
15993
+ </ion-button>
15911
15994
  </div>
15912
- <ion-modal [keepContentsMounted]="true">
15995
+ <ion-modal [trigger]="triggerId" [keepContentsMounted]="true">
15913
15996
  <ng-template>
15914
15997
  <ion-datetime
15915
15998
  [formControl]="resolvedProps.control"
15916
15999
  [id]="resolvedProps.token"
15917
- presentation="date"
16000
+ [presentation]="presentation"
15918
16001
  locale="es-ES"
15919
16002
  [firstDayOfWeek]="1"
15920
16003
  [showDefaultButtons]="true"
@@ -15930,7 +16013,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
15930
16013
  </ng-template>
15931
16014
  </ion-modal>
15932
16015
  `, styles: ["@charset \"UTF-8\";:root{--val-container-sm: 540px;--val-container-md: 720px;--val-container-lg: 880px;--val-container-xl: 1100px;--val-container-padding: 16px;--ion-color-primary: #7026df;--ion-color-primary-rgb: 112, 38, 223;--ion-color-primary-contrast: #ffffff;--ion-color-primary-contrast-rgb: 255, 255, 255;--ion-color-primary-shade: #6321c4;--ion-color-primary-tint: #7e3ce2;--ion-color-secondary: #e2ccff;--ion-color-secondary-rgb: 226, 204, 255;--ion-color-secondary-contrast: #000000;--ion-color-secondary-contrast-rgb: 0, 0, 0;--ion-color-secondary-shade: #c7b4e0;--ion-color-secondary-tint: #e5d1ff;--ion-color-texti: #354c69;--ion-color-texti-rgb: 53, 76, 105;--ion-color-texti-contrast: #ffffff;--ion-color-texti-contrast-rgb: 255, 255, 255;--ion-color-texti-shade: #2f435c;--ion-color-texti-tint: #495e78;--ion-color-darki: #090f1b;--ion-color-darki-rgb: 9, 15, 27;--ion-color-darki-contrast: #ffffff;--ion-color-darki-contrast-rgb: 255, 255, 255;--ion-color-darki-shade: #080d18;--ion-color-darki-tint: #222732;--ion-color-medium: #9e9e9e;--ion-color-medium-rgb: 158, 158, 158;--ion-color-medium-contrast: #000000;--ion-color-medium-contrast-rgb: 0, 0, 0;--ion-color-medium-shade: #8b8b8b;--ion-color-medium-tint: #a8a8a8;--swiper-pagination-color: var(--ion-color-primary);--swiper-navigation-color: var(--ion-color-primary);--swiper-pagination-bullet-inactive-color: var(--ion-color-medium)}body.dark,html.ion-palette-dark,body[data-theme=dark]{--ion-color-texti: #8fc1ff;--ion-color-texti-rgb: 143, 193, 255;--ion-color-texti-contrast: #000000;--ion-color-texti-contrast-rgb: 0, 0, 0;--ion-color-texti-shade: #7eaae0;--ion-color-texti-tint: #9ac7ff;--ion-color-darki: #ffffff;--ion-color-darki-rgb: 255, 255, 255;--ion-color-darki-contrast: #000000;--ion-color-darki-contrast-rgb: 0, 0, 0;--ion-color-darki-shade: #e0e0e0;--ion-color-darki-tint: #ffffff;--ion-color-primary: #8f49f8;--ion-color-primary-rgb: 143, 73, 248;--ion-color-primary-contrast: #ffffff;--ion-color-primary-contrast-rgb: 255, 255, 255;--ion-color-primary-shade: #7e40da;--ion-color-primary-tint: #9a5bf9}.ion-color-texti{--ion-color-base: var(--ion-color-texti);--ion-color-base-rgb: var(--ion-color-texti-rgb);--ion-color-contrast: var(--ion-color-texti-contrast);--ion-color-contrast-rgb: var(--ion-color-texti-contrast-rgb);--ion-color-shade: var(--ion-color-texti-shade);--ion-color-tint: var(--ion-color-texti-tint)}.ion-color-darki{--ion-color-base: var(--ion-color-darki);--ion-color-base-rgb: var(--ion-color-darki-rgb);--ion-color-contrast: var(--ion-color-darki-contrast);--ion-color-contrast-rgb: var(--ion-color-darki-contrast-rgb);--ion-color-shade: var(--ion-color-darki-shade);--ion-color-tint: var(--ion-color-darki-tint)}.section{margin-top:1rem}.input{margin:var(--val-form-field-gap, .5rem) 0}@media (min-width: 768px){.input{margin:var(--val-form-field-gap, .75rem) 0}}.submit-actions--sticky{position:sticky;bottom:0;z-index:1;background:var(--ion-background-color);border-top:1px solid var(--ion-color-step-150, rgba(0, 0, 0, .08));padding:.75rem 0 calc(.75rem + env(safe-area-inset-bottom))}.field-description{display:block;font-size:.75rem;color:var(--ion-color-dark);margin-bottom:.25rem;line-height:1.4}.button-container{display:flex;flex-direction:column;align-items:flex-start}.action{margin-top:.25rem}\n"] }]
15933
- }], propDecorators: { preset: [{
16016
+ }], ctorParameters: () => [], propDecorators: { preset: [{
15934
16017
  type: Input
15935
16018
  }], props: [{
15936
16019
  type: Input
@@ -40269,7 +40352,7 @@ class BottomNavComponent {
40269
40352
  }
40270
40353
  </div>
40271
40354
  </nav>
40272
- `, isInline: true, styles: [":host{display:block;position:fixed;bottom:0;left:0;right:0;z-index:100;pointer-events:none}.bottom-nav{--bottom-nav-bg: var(--ion-background-color);--bottom-nav-active: var(--ion-color-primary);--bottom-nav-inactive: var(--ion-color-medium);--bottom-nav-radius: 16px 16px 0 0;--bottom-nav-height: 56px;--bottom-nav-fab-size: 56px;--bottom-nav-max-width: var(--val-container-md, 720px);--fab-color: var(--ion-color-primary);pointer-events:auto;background:var(--bottom-nav-bg);border-radius:var(--bottom-nav-radius);height:var(--bottom-nav-height);padding:0 8px;max-width:var(--bottom-nav-max-width);margin:0 auto}@media (min-width: 576px){.bottom-nav{--bottom-nav-height: 64px}}.bottom-nav--elevated{box-shadow:0 -4px 20px #00000014}.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 255, 255, 255),.75);backdrop-filter:blur(20px) saturate(180%);-webkit-backdrop-filter:blur(20px) saturate(180%);border-top:1px solid rgba(var(--ion-text-color-rgb, 0, 0, 0),.06)}.bottom-nav--floating{margin:0 auto 8px;width:calc(100% - 32px);border-radius:32px;border:1px solid rgba(var(--ion-text-color-rgb, 0, 0, 0),.08);box-shadow:0 4px 24px #00000014,0 8px 32px #0000000a;--bottom-nav-radius: 32px}.bottom-nav--floating.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 255, 255, 255),.82);backdrop-filter:blur(28px) saturate(200%);-webkit-backdrop-filter:blur(28px) saturate(200%);border:1px solid rgba(var(--ion-text-color-rgb, 0, 0, 0),.08)}.bottom-nav--floating.bottom-nav--elevated{box-shadow:0 4px 24px #0000001a,0 12px 48px #00000014}.bottom-nav--safe-area{padding-bottom:env(safe-area-inset-bottom,0);height:calc(var(--bottom-nav-height) + env(safe-area-inset-bottom,0))}.bottom-nav--floating.bottom-nav--safe-area{margin-bottom:calc(8px + env(safe-area-inset-bottom,0));padding-bottom:0;height:var(--bottom-nav-height)}.bottom-nav__container{display:flex;align-items:center;justify-content:space-around;height:var(--bottom-nav-height);max-width:500px;margin:0 auto}.bottom-nav__tab{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;height:100%;padding:8px 4px;background:transparent;border:none;cursor:pointer;position:relative;overflow:hidden;color:var(--bottom-nav-inactive);transition:color .2s ease;max-width:80px;--ripple-color: var(--bottom-nav-active)}.bottom-nav__tab:focus-visible{outline:2px solid var(--bottom-nav-active);outline-offset:-2px;border-radius:8px}.bottom-nav__tab--active{color:var(--bottom-nav-active)}.bottom-nav__tab--disabled{opacity:.4;cursor:not-allowed;pointer-events:none}.bottom-nav__tab[data-animation=scale] .bottom-nav__tab-icon{transition:transform .2s cubic-bezier(.4,0,.2,1)}.bottom-nav__tab[data-animation=scale].bottom-nav__tab--active .bottom-nav__tab-icon{transform:scale(1.15)}.bottom-nav__tab[data-animation=fade]{transition:opacity .2s ease,color .2s ease}.bottom-nav__tab[data-animation=fade]:not(.bottom-nav__tab--active){opacity:.6}.bottom-nav__tab[data-animation=slide] .bottom-nav__tab-label{transition:transform .2s ease,opacity .2s ease;transform:translateY(6px);opacity:0}.bottom-nav__tab[data-animation=slide].bottom-nav__tab--active .bottom-nav__tab-label{transform:translateY(0);opacity:1}.bottom-nav__tab-icon{position:relative;font-size:1.5rem;line-height:1}.bottom-nav__tab-icon ion-icon{display:block}.bottom-nav__tab-label{font-size:.6875rem;font-weight:500;line-height:1.2;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%;letter-spacing:.01em}.bottom-nav__badge{position:absolute;top:-4px;right:-10px;min-width:18px;height:18px;padding:0 5px;font-size:.625rem;font-weight:600;line-height:18px;text-align:center;color:#fff;background-color:var(--ion-color-danger);border-radius:9px;box-shadow:0 1px 3px #0003}.bottom-nav__badge--dot{min-width:10px;width:10px;height:10px;padding:0;top:-2px;right:-4px;border-radius:50%}.bottom-nav__fab{position:relative;flex-shrink:0;width:var(--bottom-nav-fab-size);height:var(--bottom-nav-fab-size);margin:0 12px;margin-top:calc(var(--bottom-nav-fab-size) * -.35);background:var(--fab-color);border:none;border-radius:50%;cursor:pointer;display:flex;align-items:center;justify-content:center;color:#fff;font-size:1.75rem;box-shadow:0 4px 14px rgba(var(--ion-color-primary-rgb, 56, 128, 255),.4);transition:transform .2s cubic-bezier(.4,0,.2,1),box-shadow .2s ease;overflow:hidden;--ripple-color: rgba(255, 255, 255, .3)}.bottom-nav__fab:hover{transform:scale(1.08);box-shadow:0 6px 20px rgba(var(--ion-color-primary-rgb, 56, 128, 255),.5)}.bottom-nav__fab:active{transform:scale(.95)}.bottom-nav__fab:focus-visible{outline:3px solid white;outline-offset:2px}.bottom-nav__fab--small{--bottom-nav-fab-size: 48px;font-size:1.5rem;margin-top:-14.4px}.bottom-nav__fab ion-icon{display:block}.bottom-nav--hide-labels{--bottom-nav-height: 56px}.bottom-nav--hide-labels .bottom-nav__tab-label{display:none}.bottom-nav--hide-labels .bottom-nav__tab-icon{font-size:1.625rem}:host-context(.dark) .bottom-nav--translucent,:host-context(.ion-palette-dark) .bottom-nav--translucent,:host-context(html.ion-palette-dark) .bottom-nav--translucent,:host-context(body.dark) .bottom-nav--translucent,:host-context([data-theme=dark]) .bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 18, 18, 18),.85);border-top-color:#ffffff14}:host-context(.dark) .bottom-nav--elevated,:host-context(.ion-palette-dark) .bottom-nav--elevated,:host-context(html.ion-palette-dark) .bottom-nav--elevated,:host-context(body.dark) .bottom-nav--elevated,:host-context([data-theme=dark]) .bottom-nav--elevated{box-shadow:0 -4px 20px #0000004d}:host-context(.dark) .bottom-nav--floating,:host-context(.ion-palette-dark) .bottom-nav--floating,:host-context(html.ion-palette-dark) .bottom-nav--floating,:host-context(body.dark) .bottom-nav--floating,:host-context([data-theme=dark]) .bottom-nav--floating{border-color:#ffffff1a;box-shadow:0 4px 24px #0003,0 8px 32px #00000026}:host-context(.dark) .bottom-nav--floating.bottom-nav--translucent,:host-context(.ion-palette-dark) .bottom-nav--floating.bottom-nav--translucent,:host-context(html.ion-palette-dark) .bottom-nav--floating.bottom-nav--translucent,:host-context(body.dark) .bottom-nav--floating.bottom-nav--translucent,:host-context([data-theme=dark]) .bottom-nav--floating.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 18, 18, 18),.88);border-color:#ffffff1a}:host-context(.dark) .bottom-nav__fab,:host-context(.ion-palette-dark) .bottom-nav__fab,:host-context(html.ion-palette-dark) .bottom-nav__fab,:host-context(body.dark) .bottom-nav__fab,:host-context([data-theme=dark]) .bottom-nav__fab{box-shadow:0 4px 14px #00000080}:host-context(.dark) .bottom-nav__fab:hover,:host-context(.ion-palette-dark) .bottom-nav__fab:hover,:host-context(html.ion-palette-dark) .bottom-nav__fab:hover,:host-context(body.dark) .bottom-nav__fab:hover,:host-context([data-theme=dark]) .bottom-nav__fab:hover{box-shadow:0 6px 20px #0009}@supports (padding-bottom: env(safe-area-inset-bottom)){.bottom-nav--safe-area .bottom-nav__container{height:var(--bottom-nav-height)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$5.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: IonRippleEffect, selector: "ion-ripple-effect", inputs: ["type"] }] }); }
40355
+ `, isInline: true, styles: [":host{display:block;position:fixed;bottom:0;left:0;right:0;z-index:100;pointer-events:none}.bottom-nav{--bottom-nav-bg: var(--ion-background-color);--bottom-nav-active: var(--ion-color-primary);--bottom-nav-inactive: var(--ion-color-medium);--bottom-nav-radius: 16px 16px 0 0;--bottom-nav-height: 56px;--bottom-nav-fab-size: 56px;--bottom-nav-max-width: var(--val-container-md, 720px);--fab-color: var(--ion-color-primary);pointer-events:auto;background:var(--bottom-nav-bg);border-radius:var(--bottom-nav-radius);height:var(--bottom-nav-height);padding:0 8px;max-width:var(--bottom-nav-max-width);margin:0 auto}@media (min-width: 576px){.bottom-nav{--bottom-nav-height: 64px}}.bottom-nav--elevated{box-shadow:0 -4px 20px #00000014}.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 255, 255, 255),.75);backdrop-filter:blur(20px) saturate(180%);-webkit-backdrop-filter:blur(20px) saturate(180%);border-top:1px solid rgba(var(--ion-text-color-rgb, 0, 0, 0),.06)}.bottom-nav--floating{margin:0 auto 8px;width:calc(100% - 32px);border-radius:32px;border:1px solid rgba(var(--ion-text-color-rgb, 0, 0, 0),.08);box-shadow:0 4px 24px #00000014,0 8px 32px #0000000a;--bottom-nav-radius: 32px}.bottom-nav--floating.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 255, 255, 255),.82);backdrop-filter:blur(28px) saturate(200%);-webkit-backdrop-filter:blur(28px) saturate(200%);border:1px solid rgba(var(--ion-text-color-rgb, 0, 0, 0),.08)}.bottom-nav--floating.bottom-nav--elevated{box-shadow:0 4px 24px #0000001a,0 12px 48px #00000014}.bottom-nav--safe-area{padding-bottom:env(safe-area-inset-bottom,0);height:calc(var(--bottom-nav-height) + env(safe-area-inset-bottom,0))}.bottom-nav--floating.bottom-nav--safe-area{margin-bottom:calc(8px + env(safe-area-inset-bottom,0));padding-bottom:0;height:var(--bottom-nav-height)}.bottom-nav__container{display:flex;align-items:center;justify-content:space-around;height:var(--bottom-nav-height);max-width:500px;margin:0 auto}.bottom-nav__tab{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;height:100%;padding:8px 4px;background:transparent;border:none;cursor:pointer;position:relative;overflow:hidden;color:var(--bottom-nav-inactive);transition:color .2s ease;max-width:80px;--ripple-color: var(--bottom-nav-active)}.bottom-nav__tab:focus-visible{outline:2px solid var(--bottom-nav-active);outline-offset:-2px;border-radius:8px}.bottom-nav__tab--active{color:var(--bottom-nav-active)}.bottom-nav__tab--disabled{opacity:.4;cursor:not-allowed;pointer-events:none}.bottom-nav__tab[data-animation=scale] .bottom-nav__tab-icon{transition:transform .2s cubic-bezier(.4,0,.2,1)}.bottom-nav__tab[data-animation=scale].bottom-nav__tab--active .bottom-nav__tab-icon{transform:scale(1.15)}.bottom-nav__tab[data-animation=fade]{transition:opacity .2s ease,color .2s ease}.bottom-nav__tab[data-animation=fade]:not(.bottom-nav__tab--active){opacity:.6}.bottom-nav__tab[data-animation=slide] .bottom-nav__tab-label{transition:transform .2s ease,opacity .2s ease;transform:translateY(6px);opacity:0}.bottom-nav__tab[data-animation=slide].bottom-nav__tab--active .bottom-nav__tab-label{transform:translateY(0);opacity:1}.bottom-nav__tab-icon{position:relative;font-size:1.5rem;line-height:1}.bottom-nav__tab-icon ion-icon{display:block}.bottom-nav__tab-label{font-size:.6875rem;font-weight:500;line-height:1.2;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%;letter-spacing:.01em}.bottom-nav__badge{position:absolute;top:-4px;right:-10px;min-width:18px;height:18px;padding:0 5px;font-size:.625rem;font-weight:600;line-height:18px;text-align:center;color:#fff;background-color:var(--ion-color-danger);border-radius:9px;box-shadow:0 1px 3px #0003}.bottom-nav__badge--dot{min-width:10px;width:10px;height:10px;padding:0;top:-2px;right:-4px;border-radius:50%}.bottom-nav__fab{position:relative;flex-shrink:0;width:var(--bottom-nav-fab-size);height:var(--bottom-nav-fab-size);margin:0 12px;margin-top:calc(var(--bottom-nav-fab-size) * -.35);background:var(--fab-color);border:none;border-radius:50%;cursor:pointer;display:flex;align-items:center;justify-content:center;color:#fff;font-size:1.75rem;box-shadow:0 4px 14px rgba(var(--ion-color-primary-rgb, 56, 128, 255),.4);transition:transform .2s cubic-bezier(.4,0,.2,1),box-shadow .2s ease;overflow:hidden;--ripple-color: rgba(255, 255, 255, .3)}.bottom-nav__fab:hover{transform:scale(1.08);box-shadow:0 6px 20px rgba(var(--ion-color-primary-rgb, 56, 128, 255),.5)}.bottom-nav__fab:active{transform:scale(.95)}.bottom-nav__fab:focus-visible{outline:3px solid white;outline-offset:2px}.bottom-nav__fab--small{--bottom-nav-fab-size: 48px;font-size:1.5rem;margin-top:-14.4px}.bottom-nav__fab ion-icon{display:block}.bottom-nav--hide-labels{--bottom-nav-height: 56px}.bottom-nav--hide-labels .bottom-nav__tab-label{display:none}.bottom-nav--hide-labels .bottom-nav__tab-icon{font-size:1.625rem}:host-context(body.font-large) .bottom-nav{--bottom-nav-height: 56px}:host-context(body.font-large) .bottom-nav .bottom-nav__tab-label{display:none}:host-context(body.font-large) .bottom-nav .bottom-nav__tab-icon{font-size:1.625rem}:host-context(.dark) .bottom-nav--translucent,:host-context(.ion-palette-dark) .bottom-nav--translucent,:host-context(html.ion-palette-dark) .bottom-nav--translucent,:host-context(body.dark) .bottom-nav--translucent,:host-context([data-theme=dark]) .bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 18, 18, 18),.85);border-top-color:#ffffff14}:host-context(.dark) .bottom-nav--elevated,:host-context(.ion-palette-dark) .bottom-nav--elevated,:host-context(html.ion-palette-dark) .bottom-nav--elevated,:host-context(body.dark) .bottom-nav--elevated,:host-context([data-theme=dark]) .bottom-nav--elevated{box-shadow:0 -4px 20px #0000004d}:host-context(.dark) .bottom-nav--floating,:host-context(.ion-palette-dark) .bottom-nav--floating,:host-context(html.ion-palette-dark) .bottom-nav--floating,:host-context(body.dark) .bottom-nav--floating,:host-context([data-theme=dark]) .bottom-nav--floating{border-color:#ffffff1a;box-shadow:0 4px 24px #0003,0 8px 32px #00000026}:host-context(.dark) .bottom-nav--floating.bottom-nav--translucent,:host-context(.ion-palette-dark) .bottom-nav--floating.bottom-nav--translucent,:host-context(html.ion-palette-dark) .bottom-nav--floating.bottom-nav--translucent,:host-context(body.dark) .bottom-nav--floating.bottom-nav--translucent,:host-context([data-theme=dark]) .bottom-nav--floating.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 18, 18, 18),.88);border-color:#ffffff1a}:host-context(.dark) .bottom-nav__fab,:host-context(.ion-palette-dark) .bottom-nav__fab,:host-context(html.ion-palette-dark) .bottom-nav__fab,:host-context(body.dark) .bottom-nav__fab,:host-context([data-theme=dark]) .bottom-nav__fab{box-shadow:0 4px 14px #00000080}:host-context(.dark) .bottom-nav__fab:hover,:host-context(.ion-palette-dark) .bottom-nav__fab:hover,:host-context(html.ion-palette-dark) .bottom-nav__fab:hover,:host-context(body.dark) .bottom-nav__fab:hover,:host-context([data-theme=dark]) .bottom-nav__fab:hover{box-shadow:0 6px 20px #0009}@supports (padding-bottom: env(safe-area-inset-bottom)){.bottom-nav--safe-area .bottom-nav__container{height:var(--bottom-nav-height)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$5.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: IonRippleEffect, selector: "ion-ripple-effect", inputs: ["type"] }] }); }
40273
40356
  }
40274
40357
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: BottomNavComponent, decorators: [{
40275
40358
  type: Component,
@@ -40343,7 +40426,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
40343
40426
  }
40344
40427
  </div>
40345
40428
  </nav>
40346
- `, styles: [":host{display:block;position:fixed;bottom:0;left:0;right:0;z-index:100;pointer-events:none}.bottom-nav{--bottom-nav-bg: var(--ion-background-color);--bottom-nav-active: var(--ion-color-primary);--bottom-nav-inactive: var(--ion-color-medium);--bottom-nav-radius: 16px 16px 0 0;--bottom-nav-height: 56px;--bottom-nav-fab-size: 56px;--bottom-nav-max-width: var(--val-container-md, 720px);--fab-color: var(--ion-color-primary);pointer-events:auto;background:var(--bottom-nav-bg);border-radius:var(--bottom-nav-radius);height:var(--bottom-nav-height);padding:0 8px;max-width:var(--bottom-nav-max-width);margin:0 auto}@media (min-width: 576px){.bottom-nav{--bottom-nav-height: 64px}}.bottom-nav--elevated{box-shadow:0 -4px 20px #00000014}.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 255, 255, 255),.75);backdrop-filter:blur(20px) saturate(180%);-webkit-backdrop-filter:blur(20px) saturate(180%);border-top:1px solid rgba(var(--ion-text-color-rgb, 0, 0, 0),.06)}.bottom-nav--floating{margin:0 auto 8px;width:calc(100% - 32px);border-radius:32px;border:1px solid rgba(var(--ion-text-color-rgb, 0, 0, 0),.08);box-shadow:0 4px 24px #00000014,0 8px 32px #0000000a;--bottom-nav-radius: 32px}.bottom-nav--floating.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 255, 255, 255),.82);backdrop-filter:blur(28px) saturate(200%);-webkit-backdrop-filter:blur(28px) saturate(200%);border:1px solid rgba(var(--ion-text-color-rgb, 0, 0, 0),.08)}.bottom-nav--floating.bottom-nav--elevated{box-shadow:0 4px 24px #0000001a,0 12px 48px #00000014}.bottom-nav--safe-area{padding-bottom:env(safe-area-inset-bottom,0);height:calc(var(--bottom-nav-height) + env(safe-area-inset-bottom,0))}.bottom-nav--floating.bottom-nav--safe-area{margin-bottom:calc(8px + env(safe-area-inset-bottom,0));padding-bottom:0;height:var(--bottom-nav-height)}.bottom-nav__container{display:flex;align-items:center;justify-content:space-around;height:var(--bottom-nav-height);max-width:500px;margin:0 auto}.bottom-nav__tab{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;height:100%;padding:8px 4px;background:transparent;border:none;cursor:pointer;position:relative;overflow:hidden;color:var(--bottom-nav-inactive);transition:color .2s ease;max-width:80px;--ripple-color: var(--bottom-nav-active)}.bottom-nav__tab:focus-visible{outline:2px solid var(--bottom-nav-active);outline-offset:-2px;border-radius:8px}.bottom-nav__tab--active{color:var(--bottom-nav-active)}.bottom-nav__tab--disabled{opacity:.4;cursor:not-allowed;pointer-events:none}.bottom-nav__tab[data-animation=scale] .bottom-nav__tab-icon{transition:transform .2s cubic-bezier(.4,0,.2,1)}.bottom-nav__tab[data-animation=scale].bottom-nav__tab--active .bottom-nav__tab-icon{transform:scale(1.15)}.bottom-nav__tab[data-animation=fade]{transition:opacity .2s ease,color .2s ease}.bottom-nav__tab[data-animation=fade]:not(.bottom-nav__tab--active){opacity:.6}.bottom-nav__tab[data-animation=slide] .bottom-nav__tab-label{transition:transform .2s ease,opacity .2s ease;transform:translateY(6px);opacity:0}.bottom-nav__tab[data-animation=slide].bottom-nav__tab--active .bottom-nav__tab-label{transform:translateY(0);opacity:1}.bottom-nav__tab-icon{position:relative;font-size:1.5rem;line-height:1}.bottom-nav__tab-icon ion-icon{display:block}.bottom-nav__tab-label{font-size:.6875rem;font-weight:500;line-height:1.2;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%;letter-spacing:.01em}.bottom-nav__badge{position:absolute;top:-4px;right:-10px;min-width:18px;height:18px;padding:0 5px;font-size:.625rem;font-weight:600;line-height:18px;text-align:center;color:#fff;background-color:var(--ion-color-danger);border-radius:9px;box-shadow:0 1px 3px #0003}.bottom-nav__badge--dot{min-width:10px;width:10px;height:10px;padding:0;top:-2px;right:-4px;border-radius:50%}.bottom-nav__fab{position:relative;flex-shrink:0;width:var(--bottom-nav-fab-size);height:var(--bottom-nav-fab-size);margin:0 12px;margin-top:calc(var(--bottom-nav-fab-size) * -.35);background:var(--fab-color);border:none;border-radius:50%;cursor:pointer;display:flex;align-items:center;justify-content:center;color:#fff;font-size:1.75rem;box-shadow:0 4px 14px rgba(var(--ion-color-primary-rgb, 56, 128, 255),.4);transition:transform .2s cubic-bezier(.4,0,.2,1),box-shadow .2s ease;overflow:hidden;--ripple-color: rgba(255, 255, 255, .3)}.bottom-nav__fab:hover{transform:scale(1.08);box-shadow:0 6px 20px rgba(var(--ion-color-primary-rgb, 56, 128, 255),.5)}.bottom-nav__fab:active{transform:scale(.95)}.bottom-nav__fab:focus-visible{outline:3px solid white;outline-offset:2px}.bottom-nav__fab--small{--bottom-nav-fab-size: 48px;font-size:1.5rem;margin-top:-14.4px}.bottom-nav__fab ion-icon{display:block}.bottom-nav--hide-labels{--bottom-nav-height: 56px}.bottom-nav--hide-labels .bottom-nav__tab-label{display:none}.bottom-nav--hide-labels .bottom-nav__tab-icon{font-size:1.625rem}:host-context(.dark) .bottom-nav--translucent,:host-context(.ion-palette-dark) .bottom-nav--translucent,:host-context(html.ion-palette-dark) .bottom-nav--translucent,:host-context(body.dark) .bottom-nav--translucent,:host-context([data-theme=dark]) .bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 18, 18, 18),.85);border-top-color:#ffffff14}:host-context(.dark) .bottom-nav--elevated,:host-context(.ion-palette-dark) .bottom-nav--elevated,:host-context(html.ion-palette-dark) .bottom-nav--elevated,:host-context(body.dark) .bottom-nav--elevated,:host-context([data-theme=dark]) .bottom-nav--elevated{box-shadow:0 -4px 20px #0000004d}:host-context(.dark) .bottom-nav--floating,:host-context(.ion-palette-dark) .bottom-nav--floating,:host-context(html.ion-palette-dark) .bottom-nav--floating,:host-context(body.dark) .bottom-nav--floating,:host-context([data-theme=dark]) .bottom-nav--floating{border-color:#ffffff1a;box-shadow:0 4px 24px #0003,0 8px 32px #00000026}:host-context(.dark) .bottom-nav--floating.bottom-nav--translucent,:host-context(.ion-palette-dark) .bottom-nav--floating.bottom-nav--translucent,:host-context(html.ion-palette-dark) .bottom-nav--floating.bottom-nav--translucent,:host-context(body.dark) .bottom-nav--floating.bottom-nav--translucent,:host-context([data-theme=dark]) .bottom-nav--floating.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 18, 18, 18),.88);border-color:#ffffff1a}:host-context(.dark) .bottom-nav__fab,:host-context(.ion-palette-dark) .bottom-nav__fab,:host-context(html.ion-palette-dark) .bottom-nav__fab,:host-context(body.dark) .bottom-nav__fab,:host-context([data-theme=dark]) .bottom-nav__fab{box-shadow:0 4px 14px #00000080}:host-context(.dark) .bottom-nav__fab:hover,:host-context(.ion-palette-dark) .bottom-nav__fab:hover,:host-context(html.ion-palette-dark) .bottom-nav__fab:hover,:host-context(body.dark) .bottom-nav__fab:hover,:host-context([data-theme=dark]) .bottom-nav__fab:hover{box-shadow:0 6px 20px #0009}@supports (padding-bottom: env(safe-area-inset-bottom)){.bottom-nav--safe-area .bottom-nav__container{height:var(--bottom-nav-height)}}\n"] }]
40429
+ `, styles: [":host{display:block;position:fixed;bottom:0;left:0;right:0;z-index:100;pointer-events:none}.bottom-nav{--bottom-nav-bg: var(--ion-background-color);--bottom-nav-active: var(--ion-color-primary);--bottom-nav-inactive: var(--ion-color-medium);--bottom-nav-radius: 16px 16px 0 0;--bottom-nav-height: 56px;--bottom-nav-fab-size: 56px;--bottom-nav-max-width: var(--val-container-md, 720px);--fab-color: var(--ion-color-primary);pointer-events:auto;background:var(--bottom-nav-bg);border-radius:var(--bottom-nav-radius);height:var(--bottom-nav-height);padding:0 8px;max-width:var(--bottom-nav-max-width);margin:0 auto}@media (min-width: 576px){.bottom-nav{--bottom-nav-height: 64px}}.bottom-nav--elevated{box-shadow:0 -4px 20px #00000014}.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 255, 255, 255),.75);backdrop-filter:blur(20px) saturate(180%);-webkit-backdrop-filter:blur(20px) saturate(180%);border-top:1px solid rgba(var(--ion-text-color-rgb, 0, 0, 0),.06)}.bottom-nav--floating{margin:0 auto 8px;width:calc(100% - 32px);border-radius:32px;border:1px solid rgba(var(--ion-text-color-rgb, 0, 0, 0),.08);box-shadow:0 4px 24px #00000014,0 8px 32px #0000000a;--bottom-nav-radius: 32px}.bottom-nav--floating.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 255, 255, 255),.82);backdrop-filter:blur(28px) saturate(200%);-webkit-backdrop-filter:blur(28px) saturate(200%);border:1px solid rgba(var(--ion-text-color-rgb, 0, 0, 0),.08)}.bottom-nav--floating.bottom-nav--elevated{box-shadow:0 4px 24px #0000001a,0 12px 48px #00000014}.bottom-nav--safe-area{padding-bottom:env(safe-area-inset-bottom,0);height:calc(var(--bottom-nav-height) + env(safe-area-inset-bottom,0))}.bottom-nav--floating.bottom-nav--safe-area{margin-bottom:calc(8px + env(safe-area-inset-bottom,0));padding-bottom:0;height:var(--bottom-nav-height)}.bottom-nav__container{display:flex;align-items:center;justify-content:space-around;height:var(--bottom-nav-height);max-width:500px;margin:0 auto}.bottom-nav__tab{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;height:100%;padding:8px 4px;background:transparent;border:none;cursor:pointer;position:relative;overflow:hidden;color:var(--bottom-nav-inactive);transition:color .2s ease;max-width:80px;--ripple-color: var(--bottom-nav-active)}.bottom-nav__tab:focus-visible{outline:2px solid var(--bottom-nav-active);outline-offset:-2px;border-radius:8px}.bottom-nav__tab--active{color:var(--bottom-nav-active)}.bottom-nav__tab--disabled{opacity:.4;cursor:not-allowed;pointer-events:none}.bottom-nav__tab[data-animation=scale] .bottom-nav__tab-icon{transition:transform .2s cubic-bezier(.4,0,.2,1)}.bottom-nav__tab[data-animation=scale].bottom-nav__tab--active .bottom-nav__tab-icon{transform:scale(1.15)}.bottom-nav__tab[data-animation=fade]{transition:opacity .2s ease,color .2s ease}.bottom-nav__tab[data-animation=fade]:not(.bottom-nav__tab--active){opacity:.6}.bottom-nav__tab[data-animation=slide] .bottom-nav__tab-label{transition:transform .2s ease,opacity .2s ease;transform:translateY(6px);opacity:0}.bottom-nav__tab[data-animation=slide].bottom-nav__tab--active .bottom-nav__tab-label{transform:translateY(0);opacity:1}.bottom-nav__tab-icon{position:relative;font-size:1.5rem;line-height:1}.bottom-nav__tab-icon ion-icon{display:block}.bottom-nav__tab-label{font-size:.6875rem;font-weight:500;line-height:1.2;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%;letter-spacing:.01em}.bottom-nav__badge{position:absolute;top:-4px;right:-10px;min-width:18px;height:18px;padding:0 5px;font-size:.625rem;font-weight:600;line-height:18px;text-align:center;color:#fff;background-color:var(--ion-color-danger);border-radius:9px;box-shadow:0 1px 3px #0003}.bottom-nav__badge--dot{min-width:10px;width:10px;height:10px;padding:0;top:-2px;right:-4px;border-radius:50%}.bottom-nav__fab{position:relative;flex-shrink:0;width:var(--bottom-nav-fab-size);height:var(--bottom-nav-fab-size);margin:0 12px;margin-top:calc(var(--bottom-nav-fab-size) * -.35);background:var(--fab-color);border:none;border-radius:50%;cursor:pointer;display:flex;align-items:center;justify-content:center;color:#fff;font-size:1.75rem;box-shadow:0 4px 14px rgba(var(--ion-color-primary-rgb, 56, 128, 255),.4);transition:transform .2s cubic-bezier(.4,0,.2,1),box-shadow .2s ease;overflow:hidden;--ripple-color: rgba(255, 255, 255, .3)}.bottom-nav__fab:hover{transform:scale(1.08);box-shadow:0 6px 20px rgba(var(--ion-color-primary-rgb, 56, 128, 255),.5)}.bottom-nav__fab:active{transform:scale(.95)}.bottom-nav__fab:focus-visible{outline:3px solid white;outline-offset:2px}.bottom-nav__fab--small{--bottom-nav-fab-size: 48px;font-size:1.5rem;margin-top:-14.4px}.bottom-nav__fab ion-icon{display:block}.bottom-nav--hide-labels{--bottom-nav-height: 56px}.bottom-nav--hide-labels .bottom-nav__tab-label{display:none}.bottom-nav--hide-labels .bottom-nav__tab-icon{font-size:1.625rem}:host-context(body.font-large) .bottom-nav{--bottom-nav-height: 56px}:host-context(body.font-large) .bottom-nav .bottom-nav__tab-label{display:none}:host-context(body.font-large) .bottom-nav .bottom-nav__tab-icon{font-size:1.625rem}:host-context(.dark) .bottom-nav--translucent,:host-context(.ion-palette-dark) .bottom-nav--translucent,:host-context(html.ion-palette-dark) .bottom-nav--translucent,:host-context(body.dark) .bottom-nav--translucent,:host-context([data-theme=dark]) .bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 18, 18, 18),.85);border-top-color:#ffffff14}:host-context(.dark) .bottom-nav--elevated,:host-context(.ion-palette-dark) .bottom-nav--elevated,:host-context(html.ion-palette-dark) .bottom-nav--elevated,:host-context(body.dark) .bottom-nav--elevated,:host-context([data-theme=dark]) .bottom-nav--elevated{box-shadow:0 -4px 20px #0000004d}:host-context(.dark) .bottom-nav--floating,:host-context(.ion-palette-dark) .bottom-nav--floating,:host-context(html.ion-palette-dark) .bottom-nav--floating,:host-context(body.dark) .bottom-nav--floating,:host-context([data-theme=dark]) .bottom-nav--floating{border-color:#ffffff1a;box-shadow:0 4px 24px #0003,0 8px 32px #00000026}:host-context(.dark) .bottom-nav--floating.bottom-nav--translucent,:host-context(.ion-palette-dark) .bottom-nav--floating.bottom-nav--translucent,:host-context(html.ion-palette-dark) .bottom-nav--floating.bottom-nav--translucent,:host-context(body.dark) .bottom-nav--floating.bottom-nav--translucent,:host-context([data-theme=dark]) .bottom-nav--floating.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 18, 18, 18),.88);border-color:#ffffff1a}:host-context(.dark) .bottom-nav__fab,:host-context(.ion-palette-dark) .bottom-nav__fab,:host-context(html.ion-palette-dark) .bottom-nav__fab,:host-context(body.dark) .bottom-nav__fab,:host-context([data-theme=dark]) .bottom-nav__fab{box-shadow:0 4px 14px #00000080}:host-context(.dark) .bottom-nav__fab:hover,:host-context(.ion-palette-dark) .bottom-nav__fab:hover,:host-context(html.ion-palette-dark) .bottom-nav__fab:hover,:host-context(body.dark) .bottom-nav__fab:hover,:host-context([data-theme=dark]) .bottom-nav__fab:hover{box-shadow:0 6px 20px #0009}@supports (padding-bottom: env(safe-area-inset-bottom)){.bottom-nav--safe-area .bottom-nav__container{height:var(--bottom-nav-height)}}\n"] }]
40347
40430
  }], propDecorators: { tabClick: [{
40348
40431
  type: Output
40349
40432
  }], fabClick: [{
@@ -41652,7 +41735,7 @@ const PROFILE_VIEW_I18N = {
41652
41735
  const HANDLE_PATTERN = /^[a-z_][a-z0-9_]*$/;
41653
41736
  const HANDLE_MIN = 3;
41654
41737
  const HANDLE_MAX = 20;
41655
- const DEFAULT_NAMESPACE$2 = 'Settings.Profile';
41738
+ const DEFAULT_NAMESPACE$7 = 'Settings.Profile';
41656
41739
  /**
41657
41740
  * `val-profile-view` — vista Perfil full-feature autocontenida (organism).
41658
41741
  *
@@ -41702,7 +41785,7 @@ class ProfileViewComponent {
41702
41785
  showPhone: merged.showPhone ?? true,
41703
41786
  showShareCta: merged.showShareCta ?? true,
41704
41787
  profileBaseUrl: merged.profileBaseUrl ?? '',
41705
- i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$2,
41788
+ i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$7,
41706
41789
  onSaved: merged.onSaved,
41707
41790
  onAvatarUploaded: merged.onAvatarUploaded,
41708
41791
  };
@@ -42342,6 +42425,8 @@ class FontSizeService {
42342
42425
  if (!this.isBrowser)
42343
42426
  return;
42344
42427
  document.documentElement.style.fontSize = SIZE_PX[size];
42428
+ document.body.classList.toggle('font-large', size === FontSizeOption.LARGE);
42429
+ document.body.classList.toggle('font-small', size === FontSizeOption.SMALL);
42345
42430
  }
42346
42431
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: FontSizeService, deps: [{ token: PLATFORM_ID }], target: i0.ɵɵFactoryTarget.Injectable }); }
42347
42432
  static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: FontSizeService, providedIn: 'root' }); }
@@ -42616,7 +42701,7 @@ const PREFERENCES_VIEW_I18N = {
42616
42701
  },
42617
42702
  };
42618
42703
 
42619
- const DEFAULT_NAMESPACE$1 = 'Settings.General';
42704
+ const DEFAULT_NAMESPACE$6 = 'Settings.General';
42620
42705
  const DEFAULT_LANGUAGES = ['es', 'en'];
42621
42706
  /**
42622
42707
  * `val-preferences-view` — vista de preferencias de UI cross-app (tema +
@@ -42653,7 +42738,7 @@ class PreferencesViewComponent {
42653
42738
  showLanguage: merged.showLanguage ?? true,
42654
42739
  showFontSize: merged.showFontSize ?? true,
42655
42740
  supportedLanguages: merged.supportedLanguages ?? DEFAULT_LANGUAGES,
42656
- i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$1,
42741
+ i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$6,
42657
42742
  onThemeChanged: merged.onThemeChanged,
42658
42743
  onLanguageChanged: merged.onLanguageChanged,
42659
42744
  onFontSizeChanged: merged.onFontSizeChanged,
@@ -44465,7 +44550,7 @@ const SECURITY_VIEW_I18N = {
44465
44550
  };
44466
44551
 
44467
44552
  addIcons({ laptopOutline, phonePortraitOutline });
44468
- const DEFAULT_NAMESPACE = 'Settings.Security';
44553
+ const DEFAULT_NAMESPACE$5 = 'Settings.Security';
44469
44554
  /**
44470
44555
  * `val-security-view` — vista Seguridad full-feature autocontenida (organism).
44471
44556
  *
@@ -44514,7 +44599,7 @@ class SecurityViewComponent {
44514
44599
  showChangeEmail: merged.showChangeEmail ?? true,
44515
44600
  showMfa: merged.showMfa ?? true,
44516
44601
  showPolicies: merged.showPolicies ?? true,
44517
- i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE,
44602
+ i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$5,
44518
44603
  handleDeepLinks: merged.handleDeepLinks ?? true,
44519
44604
  homeRoute: merged.homeRoute ?? '/app/home',
44520
44605
  onMfaCompleted: merged.onMfaCompleted,
@@ -45278,187 +45363,2198 @@ function provideValtechSecurityRoutes(opts) {
45278
45363
  ];
45279
45364
  }
45280
45365
 
45281
- /** Built-in label sets for the three platform locales. */
45282
- const CALLOUT_LABELS = {
45366
+ class OrgService {
45367
+ constructor(config, http) {
45368
+ this.config = config;
45369
+ this.http = http;
45370
+ }
45371
+ get baseUrl() {
45372
+ return `${this.config.apiUrl}/org`;
45373
+ }
45374
+ listMyOrgs() {
45375
+ return this.http.get(`${this.baseUrl}/my`).pipe(map$1(r => r.organizations));
45376
+ }
45377
+ listMyOrgsPage(params) {
45378
+ const queryParams = new URLSearchParams();
45379
+ if (params?.limit)
45380
+ queryParams.set('limit', String(params.limit));
45381
+ if (params?.nextToken)
45382
+ queryParams.set('nextToken', params.nextToken);
45383
+ const qs = queryParams.toString();
45384
+ const url = `${this.baseUrl}/my${qs ? '?' + qs : ''}`;
45385
+ return this.http.get(url);
45386
+ }
45387
+ getOrg(orgId) {
45388
+ return this.http.get(`${this.baseUrl}/${orgId}`).pipe(map$1(r => r.organization));
45389
+ }
45390
+ createOrg(req) {
45391
+ return this.http.post(`${this.baseUrl}`, req).pipe(map$1(r => r.organization));
45392
+ }
45393
+ updateOrg(orgId, req) {
45394
+ return this.http
45395
+ .put(`${this.baseUrl}/${orgId}`, req)
45396
+ .pipe(map$1(r => r.organization));
45397
+ }
45398
+ deleteOrg(orgId) {
45399
+ return this.http.delete(`${this.baseUrl}/${orgId}`);
45400
+ }
45401
+ inviteUser(orgId, req) {
45402
+ return this.http.post(`${this.baseUrl}/${orgId}/invite`, req);
45403
+ }
45404
+ leaveOrg(orgId) {
45405
+ return this.http.post(`${this.baseUrl}/${orgId}/leave`, {});
45406
+ }
45407
+ getOrgMembers(orgId, params) {
45408
+ const queryParams = new URLSearchParams();
45409
+ if (params?.limit)
45410
+ queryParams.set('limit', String(params.limit));
45411
+ if (params?.nextToken)
45412
+ queryParams.set('nextToken', params.nextToken);
45413
+ const qs = queryParams.toString();
45414
+ const url = `${this.baseUrl}/${orgId}/members${qs ? '?' + qs : ''}`;
45415
+ return this.http.get(url);
45416
+ }
45417
+ listOrgRoles(orgId) {
45418
+ return this.http
45419
+ .get(`${this.baseUrl}/${orgId}/roles`)
45420
+ .pipe(map$1(r => r.roles ?? []));
45421
+ }
45422
+ changeMemberRole(orgId, userId, roleId) {
45423
+ const body = { roleId };
45424
+ return this.http.put(`${this.baseUrl}/${orgId}/members/${userId}/role`, body);
45425
+ }
45426
+ removeMember(orgId, userId) {
45427
+ return this.http.delete(`${this.baseUrl}/${orgId}/members/${userId}`);
45428
+ }
45429
+ transferOwnership(orgId, newOwnerId) {
45430
+ return this.http
45431
+ .post(`${this.baseUrl}/${orgId}/transfer-ownership`, { newOwnerId })
45432
+ .pipe(map$1(r => r.organization));
45433
+ }
45434
+ validateInviteToken(token) {
45435
+ return this.http.post(`${this.config.apiUrl}/v2/actions/validate`, { token });
45436
+ }
45437
+ executeInviteToken(token) {
45438
+ return this.http.post(`${this.config.apiUrl}/v2/actions/execute`, { token });
45439
+ }
45440
+ getPendingInvitations() {
45441
+ return this.http
45442
+ .get(`${this.config.apiUrl}/organizations/invitations/pending`)
45443
+ .pipe(map$1(r => r.items ?? []));
45444
+ }
45445
+ acceptInvitation(orgId) {
45446
+ return this.http.post(`${this.baseUrl}/invitations/${orgId}/accept`, {});
45447
+ }
45448
+ declineInvitation(orgId) {
45449
+ return this.http.post(`${this.baseUrl}/invitations/${orgId}/decline`, {});
45450
+ }
45451
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: OrgService, deps: [{ token: VALTECH_AUTH_CONFIG }, { token: i1$3.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable }); }
45452
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: OrgService, providedIn: 'root' }); }
45453
+ }
45454
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: OrgService, decorators: [{
45455
+ type: Injectable,
45456
+ args: [{ providedIn: 'root' }]
45457
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
45458
+ type: Inject,
45459
+ args: [VALTECH_AUTH_CONFIG]
45460
+ }] }, { type: i1$3.HttpClient }] });
45461
+
45462
+ /**
45463
+ * Defaults i18n (es/en) embebidos en `val-create-org-modal`. Auto-registrados en
45464
+ * el constructor del componente si el consumer no proveyó el namespace
45465
+ * (`CreateOrgModal` por default). SOLO incluye las keys que el modal usa.
45466
+ */
45467
+ const CREATE_ORG_MODAL_I18N = {
45283
45468
  es: {
45284
- NOTE: 'Nota',
45285
- TIP: 'Tip',
45286
- INFO: 'Info',
45287
- IMPORTANT: 'Importante',
45288
- WARNING: 'Atención',
45289
- CAUTION: 'Precaución',
45469
+ pageTitle: 'Nueva organización',
45470
+ pageDescription: 'Crea una nueva organización',
45471
+ nameLabel: 'Nombre',
45472
+ namePlaceholder: 'Nombre de la organización',
45473
+ typeLabel: 'Tipo',
45474
+ descriptionLabel: 'Descripción',
45475
+ descriptionPlaceholder: 'Opcional',
45476
+ createCta: 'Crear organización',
45477
+ close: 'Cancelar',
45478
+ nameRequired: 'El nombre es obligatorio',
45479
+ nameMin: 'Mínimo 2 caracteres',
45480
+ typePersonal: 'Personal',
45481
+ typePrivate: 'Privada',
45482
+ typePublic: 'Pública',
45483
+ typeBusiness: 'Empresa',
45484
+ successMsg: 'Organización creada. Cambiando...',
45485
+ fillRequired: 'Completa todos los campos requeridos.',
45290
45486
  },
45291
45487
  en: {
45292
- NOTE: 'Note',
45293
- TIP: 'Tip',
45294
- INFO: 'Info',
45295
- IMPORTANT: 'Important',
45296
- WARNING: 'Warning',
45297
- CAUTION: 'Caution',
45298
- },
45299
- pt: {
45300
- NOTE: 'Nota',
45301
- TIP: 'Dica',
45302
- INFO: 'Info',
45303
- IMPORTANT: 'Importante',
45304
- WARNING: 'Atenção',
45305
- CAUTION: 'Cuidado',
45488
+ pageTitle: 'New organization',
45489
+ pageDescription: 'Create a new organization',
45490
+ nameLabel: 'Name',
45491
+ namePlaceholder: 'Organization name',
45492
+ typeLabel: 'Type',
45493
+ descriptionLabel: 'Description',
45494
+ descriptionPlaceholder: 'Optional',
45495
+ createCta: 'Create organization',
45496
+ close: 'Cancel',
45497
+ nameRequired: 'Name is required',
45498
+ nameMin: 'Minimum 2 characters',
45499
+ typePersonal: 'Personal',
45500
+ typePrivate: 'Private',
45501
+ typePublic: 'Public',
45502
+ typeBusiness: 'Business',
45503
+ successMsg: 'Organization created. Switching...',
45504
+ fillRequired: 'Please fill in all required fields.',
45306
45505
  },
45307
45506
  };
45507
+
45508
+ const DEFAULT_NAMESPACE$4 = 'CreateOrgModal';
45308
45509
  /**
45309
- * Per-callout-kind color (Ionic semantic color). Fixed across locales since
45310
- * red = danger no matter what language you speak.
45510
+ * `val-create-org-modal` modal de creación de organización (organism
45511
+ * compartido). Promovido desde `showcase` bajo el proceso de ADR-021. Reusado por
45512
+ * la vista Cuenta (y futuras vistas org-aware).
45513
+ *
45514
+ * Crea la org vía `OrgService.createOrg` y, en éxito, cambia a ella con
45515
+ * `OrgSwitchService.switchTo`. El formulario (nombre + tipo + descripción) se
45516
+ * construye una vez y se mantiene reactivo a idioma + estado de trabajo vía
45517
+ * effects `allowSignalWrites`.
45518
+ *
45519
+ * Abierto vía `ModalService` — recibe `_modalRef` por `componentProps` + callback
45520
+ * `onSuccess(newOrg)`. Header canónico (Regla #5): sin `ion-title`, botón Cerrar
45521
+ * texto en `slot=end`, título/subtítulo en el body con `val-display`/`val-title`.
45522
+ *
45523
+ * Auto-registra sus defaults i18n (es/en) si el consumer no proveyó el namespace
45524
+ * `CreateOrgModal`.
45311
45525
  */
45312
- const CALLOUT_COLORS = {
45313
- NOTE: 'primary',
45314
- TIP: 'success',
45315
- INFO: 'tertiary',
45316
- IMPORTANT: 'warning',
45317
- WARNING: 'warning',
45318
- CAUTION: 'danger',
45526
+ class CreateOrgModalComponent {
45527
+ constructor() {
45528
+ this.i18n = inject(I18nService);
45529
+ this.orgService = inject(OrgService);
45530
+ this.orgSwitch = inject(OrgSwitchService);
45531
+ this.toast = inject(ToastService);
45532
+ this.errors = inject(ValtechErrorService);
45533
+ /** Namespace i18n con que la vista resuelve sus textos. */
45534
+ this.i18nNamespace = DEFAULT_NAMESPACE$4;
45535
+ this._busy = signal(false);
45536
+ if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$4)) {
45537
+ this.i18n.registerContent(DEFAULT_NAMESPACE$4, CREATE_ORG_MODAL_I18N);
45538
+ }
45539
+ this.formMeta = signal(this.buildFormMeta());
45540
+ effect(() => {
45541
+ this.i18n.lang();
45542
+ this.formMeta.set(this.buildFormMeta());
45543
+ }, { allowSignalWrites: true });
45544
+ effect(() => {
45545
+ const working = this._busy();
45546
+ this.formMeta.update(meta => ({
45547
+ ...meta,
45548
+ state: working ? ComponentStates.WORKING : ComponentStates.ENABLED,
45549
+ actions: {
45550
+ ...meta.actions,
45551
+ state: working ? ComponentStates.WORKING : ComponentStates.ENABLED,
45552
+ },
45553
+ }));
45554
+ }, { allowSignalWrites: true });
45555
+ }
45556
+ async onSubmit(event) {
45557
+ if (this._busy())
45558
+ return;
45559
+ const name = event.fields['name'];
45560
+ const type = event.fields['type'];
45561
+ if (!name?.trim() || !type) {
45562
+ this.toast.show({
45563
+ message: this.t('fillRequired'),
45564
+ duration: 3000,
45565
+ color: 'dark',
45566
+ position: 'top',
45567
+ });
45568
+ return;
45569
+ }
45570
+ this._busy.set(true);
45571
+ try {
45572
+ const description = event.fields['description'];
45573
+ const newOrg = await firstValueFrom(this.orgService.createOrg({ name, type, description }));
45574
+ this.toast.show({
45575
+ message: this.t('successMsg'),
45576
+ color: 'dark',
45577
+ duration: 3500,
45578
+ });
45579
+ await this.orgSwitch.switchTo(newOrg.id);
45580
+ this.onSuccess?.(newOrg);
45581
+ this._modalRef?.dismiss(newOrg, 'confirm');
45582
+ }
45583
+ catch (err) {
45584
+ this.errors.handle(err, { i18nNamespace: this.i18nNamespace });
45585
+ }
45586
+ finally {
45587
+ this._busy.set(false);
45588
+ }
45589
+ }
45590
+ dismiss() {
45591
+ this._modalRef?.dismiss(null, 'cancel');
45592
+ }
45593
+ buildFormMeta() {
45594
+ const nameField = {
45595
+ token: 'new-org-name',
45596
+ name: 'name',
45597
+ label: this.t('nameLabel'),
45598
+ hint: '',
45599
+ placeholder: this.t('namePlaceholder'),
45600
+ type: InputType.TEXT,
45601
+ order: 1,
45602
+ validators: [Validators.required, Validators.minLength(2), Validators.maxLength(100)],
45603
+ errors: {
45604
+ required: this.t('nameRequired'),
45605
+ minlength: this.t('nameMin'),
45606
+ },
45607
+ value: '',
45608
+ state: ComponentStates.ENABLED,
45609
+ };
45610
+ const typeField = {
45611
+ token: 'new-org-type',
45612
+ name: 'type',
45613
+ label: this.t('typeLabel'),
45614
+ hint: '',
45615
+ placeholder: '',
45616
+ type: InputType.SELECT,
45617
+ order: 2,
45618
+ validators: [Validators.required],
45619
+ errors: {},
45620
+ value: 'PERSONAL',
45621
+ withDefault: 'PERSONAL',
45622
+ state: ComponentStates.ENABLED,
45623
+ options: [
45624
+ { id: 'PERSONAL', name: this.t('typePersonal'), order: 1 },
45625
+ { id: 'PRIVATE', name: this.t('typePrivate'), order: 2 },
45626
+ { id: 'PUBLIC', name: this.t('typePublic'), order: 3 },
45627
+ { id: 'BUSINESS', name: this.t('typeBusiness'), order: 4 },
45628
+ ],
45629
+ };
45630
+ const descriptionField = {
45631
+ token: 'new-org-description',
45632
+ name: 'description',
45633
+ label: this.t('descriptionLabel'),
45634
+ hint: '',
45635
+ placeholder: this.t('descriptionPlaceholder'),
45636
+ type: InputType.TEXTAREA,
45637
+ order: 3,
45638
+ validators: [],
45639
+ errors: {},
45640
+ value: '',
45641
+ state: ComponentStates.ENABLED,
45642
+ };
45643
+ const submitButton = {
45644
+ text: this.t('createCta'),
45645
+ color: 'dark',
45646
+ type: 'submit',
45647
+ fill: 'solid',
45648
+ size: 'default',
45649
+ shape: 'round',
45650
+ expand: 'block',
45651
+ state: ComponentStates.ENABLED,
45652
+ token: 'new-org-submit',
45653
+ };
45654
+ return {
45655
+ name: '',
45656
+ state: ComponentStates.ENABLED,
45657
+ sections: [
45658
+ {
45659
+ name: '',
45660
+ order: 1,
45661
+ fields: [nameField, typeField, descriptionField],
45662
+ },
45663
+ ],
45664
+ actions: submitButton,
45665
+ };
45666
+ }
45667
+ t(key) {
45668
+ return this.i18n.t(key, this.i18nNamespace);
45669
+ }
45670
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: CreateOrgModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
45671
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: CreateOrgModalComponent, isStandalone: true, selector: "val-create-org-modal", inputs: { _modalRef: "_modalRef", onSuccess: "onSuccess", i18nNamespace: "i18nNamespace" }, ngImport: i0, template: `
45672
+ <ion-header>
45673
+ <ion-toolbar>
45674
+ <ion-buttons slot="end">
45675
+ <ion-button fill="clear" color="dark" (click)="dismiss()">
45676
+ <strong>{{ t('close') }}</strong>
45677
+ </ion-button>
45678
+ </ion-buttons>
45679
+ </ion-toolbar>
45680
+ </ion-header>
45681
+ <ion-content class="ion-padding">
45682
+ <val-display [props]="{ content: t('pageTitle'), size: 'small', color: 'dark' }" />
45683
+ <val-title
45684
+ [props]="{
45685
+ content: t('pageDescription'),
45686
+ size: 'large',
45687
+ color: '',
45688
+ bold: false,
45689
+ }"
45690
+ />
45691
+ <val-form [props]="formMeta()" (onSubmit)="onSubmit($event)" />
45692
+ </ion-content>
45693
+ `, isInline: true, dependencies: [{ kind: "component", type: IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "component", type: IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: FormComponent, selector: "val-form", inputs: ["props"], outputs: ["onSubmit", "onInvalid", "onSelectChange"] }, { kind: "component", type: DisplayComponent, selector: "val-display", inputs: ["props"] }, { kind: "component", type: TitleComponent, selector: "val-title", inputs: ["props"] }] }); }
45694
+ }
45695
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: CreateOrgModalComponent, decorators: [{
45696
+ type: Component,
45697
+ args: [{
45698
+ selector: 'val-create-org-modal',
45699
+ standalone: true,
45700
+ imports: [IonHeader, IonToolbar, IonButtons, IonButton, IonContent, FormComponent, DisplayComponent, TitleComponent],
45701
+ template: `
45702
+ <ion-header>
45703
+ <ion-toolbar>
45704
+ <ion-buttons slot="end">
45705
+ <ion-button fill="clear" color="dark" (click)="dismiss()">
45706
+ <strong>{{ t('close') }}</strong>
45707
+ </ion-button>
45708
+ </ion-buttons>
45709
+ </ion-toolbar>
45710
+ </ion-header>
45711
+ <ion-content class="ion-padding">
45712
+ <val-display [props]="{ content: t('pageTitle'), size: 'small', color: 'dark' }" />
45713
+ <val-title
45714
+ [props]="{
45715
+ content: t('pageDescription'),
45716
+ size: 'large',
45717
+ color: '',
45718
+ bold: false,
45719
+ }"
45720
+ />
45721
+ <val-form [props]="formMeta()" (onSubmit)="onSubmit($event)" />
45722
+ </ion-content>
45723
+ `,
45724
+ }]
45725
+ }], ctorParameters: () => [], propDecorators: { _modalRef: [{
45726
+ type: Input
45727
+ }], onSuccess: [{
45728
+ type: Input
45729
+ }], i18nNamespace: [{
45730
+ type: Input
45731
+ }] } });
45732
+
45733
+ /**
45734
+ * Defaults i18n (es/en) embebidos en `val-delete-account-modal`. Auto-registrados
45735
+ * en el constructor del componente si el consumer no proveyó el namespace
45736
+ * (`Settings.DeleteAccount` por default). SOLO incluye las keys que el modal usa.
45737
+ */
45738
+ const DELETE_ACCOUNT_MODAL_I18N = {
45739
+ es: {
45740
+ title: 'Eliminar cuenta',
45741
+ warningPassword: 'Esta acción es permanente y no se puede deshacer. Se eliminarán tu cuenta, sesiones activas, organizaciones personales y todos los datos asociados.',
45742
+ passwordLabel: 'Contraseña actual',
45743
+ passwordPlaceholder: 'Ingresa tu contraseña',
45744
+ passwordRequired: 'La contraseña es requerida',
45745
+ warningSendCode: 'Tu cuenta está vinculada a un proveedor externo. Para confirmar la eliminación, enviaremos un código de verificación a tu correo electrónico.',
45746
+ sendCodeCta: 'Enviar código de confirmación',
45747
+ warningEnterCode: 'Ingresa el código que enviamos a tu correo. Expira en 15 minutos.',
45748
+ codeLabel: 'Código de verificación',
45749
+ codePlaceholder: 'Código de 6 dígitos',
45750
+ codeRequired: 'El código es requerido',
45751
+ confirmCta: 'Eliminar mi cuenta',
45752
+ close: 'Cancelar',
45753
+ resend: 'Reenviar código',
45754
+ },
45755
+ en: {
45756
+ title: 'Delete account',
45757
+ warningPassword: 'This action is permanent and cannot be undone. Your account, active sessions, personal organizations and all associated data will be permanently deleted.',
45758
+ passwordLabel: 'Current password',
45759
+ passwordPlaceholder: 'Enter your password',
45760
+ passwordRequired: 'Password is required',
45761
+ warningSendCode: "Your account is linked to an external provider. To confirm deletion, we'll send a verification code to your email address.",
45762
+ sendCodeCta: 'Send confirmation code',
45763
+ warningEnterCode: 'Enter the code we sent to your email. It expires in 15 minutes.',
45764
+ codeLabel: 'Verification code',
45765
+ codePlaceholder: '6-digit code',
45766
+ codeRequired: 'Code is required',
45767
+ confirmCta: 'Delete my account',
45768
+ close: 'Cancel',
45769
+ resend: 'Resend code',
45770
+ },
45319
45771
  };
45320
- const BOX_DRAWING = /[┌┐└┘├┤┬┴┼─│╔╗╚╝═║]/;
45321
- const CALLOUT_RE = /^>\s*\[!(NOTE|TIP|INFO|WARNING|CAUTION|IMPORTANT)\]\s*(.*)$/i;
45322
- const FENCE_RE = /^```([\w-]*)\s*$/;
45323
- const HEADING_RE = /^(#{1,6})\s+(.+)$/;
45324
- const SEPARATOR_RE = /^\s*(?:-{3,}|\*{3,}|_{3,})\s*$/;
45325
- const UNORDERED_RE = /^\s*[-*+]\s+(.+)$/;
45326
- const CHECKLIST_RE = /^\s*[-*+]\s+\[([ xX])\]\s+(.+)$/;
45327
- const ORDERED_RE = /^\s*\d+\.\s+(.+)$/;
45328
- const TABLE_DIVIDER_RE = /^\s*\|?[\s:|-]+\|?\s*$/;
45329
- const TABLE_ROW_RE = /^\s*\|(.+)\|\s*$/;
45772
+
45773
+ const DEFAULT_NAMESPACE$3 = 'Settings.DeleteAccount';
45330
45774
  /**
45331
- * Pure Markdown ArticleMetadata parser. No Angular deps — usable from Node scripts
45332
- * (build-time generation) and from the Angular `MarkdownArticleParserService` wrapper.
45775
+ * `val-delete-account-modal` modal de eliminación de cuenta (organism,
45776
+ * cuenta-específico). Promovido desde `showcase` bajo el proceso de ADR-021.
45333
45777
  *
45334
- * Supported syntax:
45335
- * - Headings (#, ##, ### …) title / subtitle
45336
- * - Paragraphs → paragraph (with `processLinks` + `allowPartialBold`)
45337
- * - Lists (-, *, 1.) → unordered / ordered list (- [ ] / - [x] for checklist)
45338
- * - Blockquotes (>) quote, with GitHub callouts `> [!NOTE|TIP|INFO|WARNING|CAUTION|IMPORTANT]` mapped to notes
45339
- * - Code fences (```lang) → code
45340
- * - Box-drawing ASCII art (┌┐└┘│─) auto-wrapped as code
45341
- * - Tables flattened into paragraphs with bold keys
45342
- * - Horizontal rules (---, ***, ___) separator
45778
+ * Decide el flujo según si la cuenta tiene contraseña:
45779
+ * - **con password** pide la contraseña actual y la verifica vía
45780
+ * `auth.deleteAccount({ password })`.
45781
+ * - **sin password** (cuenta OAuth) → envía un código por email
45782
+ * (`auth.sendDeleteAccountCode()`) y lo pide para confirmar
45783
+ * (`auth.deleteAccount({ code })`).
45784
+ *
45785
+ * Abierto vía `ModalService` recibe `_modalRef` por `componentProps` para
45786
+ * cerrar desde dentro. Header canónico (Regla #5): sin `ion-title`, botón Cerrar
45787
+ * texto en `slot=end`, título/subtítulo en el body vía `val-form`.
45788
+ *
45789
+ * Auto-registra sus defaults i18n (es/en) en el constructor si el consumer no
45790
+ * proveyó el namespace `Settings.DeleteAccount`.
45343
45791
  */
45344
- function parseMarkdownArticle(markdown, options) {
45345
- const lines = normalize(markdown).split('\n');
45346
- const elements = [];
45347
- const labels = options?.calloutLabels ??
45348
- (options?.locale ? CALLOUT_LABELS[options.locale] : CALLOUT_LABELS.es);
45349
- let i = 0;
45350
- while (i < lines.length) {
45351
- const line = lines[i];
45352
- if (line.trim() === '') {
45353
- i++;
45354
- continue;
45792
+ class DeleteAccountModalComponent {
45793
+ constructor() {
45794
+ this.auth = inject(AuthService);
45795
+ this.errors = inject(ValtechErrorService);
45796
+ this.i18n = inject(I18nService);
45797
+ this.toast = inject(ToastService);
45798
+ /** Namespace i18n con que la vista resuelve sus textos. */
45799
+ this.i18nNamespace = DEFAULT_NAMESPACE$3;
45800
+ this.hasPassword = signal(null);
45801
+ this.codeSent = signal(false);
45802
+ this.formState = signal(ComponentStates.ENABLED);
45803
+ this.t = (key) => this.i18n.t(key, this.i18nNamespace);
45804
+ this.passwordFormProps = computed(() => ({
45805
+ name: this.t('title'),
45806
+ state: this.formState(),
45807
+ sections: [
45808
+ {
45809
+ name: this.t('warningPassword'),
45810
+ order: 0,
45811
+ fields: [
45812
+ {
45813
+ token: 'delete-password',
45814
+ name: 'password',
45815
+ label: this.t('passwordLabel'),
45816
+ hint: '',
45817
+ placeholder: this.t('passwordPlaceholder'),
45818
+ type: InputType.PASSWORD,
45819
+ order: 0,
45820
+ validators: [Validators.required],
45821
+ errors: { required: this.t('passwordRequired') },
45822
+ state: ComponentStates.ENABLED,
45823
+ },
45824
+ ],
45825
+ },
45826
+ ],
45827
+ actions: this.submitBtn('delete-password-submit', 'confirmCta'),
45828
+ }));
45829
+ this.sendCodeFormProps = computed(() => ({
45830
+ name: this.t('title'),
45831
+ state: this.formState(),
45832
+ sections: [
45833
+ {
45834
+ name: this.t('warningSendCode'),
45835
+ order: 0,
45836
+ fields: [],
45837
+ },
45838
+ ],
45839
+ actions: this.submitBtn('delete-send-code-submit', 'sendCodeCta'),
45840
+ }));
45841
+ this.codeFormProps = computed(() => ({
45842
+ name: this.t('title'),
45843
+ state: this.formState(),
45844
+ sections: [
45845
+ {
45846
+ name: this.t('warningEnterCode'),
45847
+ order: 0,
45848
+ fields: [
45849
+ {
45850
+ token: 'delete-code',
45851
+ name: 'code',
45852
+ label: this.t('codeLabel'),
45853
+ hint: '',
45854
+ placeholder: this.t('codePlaceholder'),
45855
+ type: InputType.TEXT,
45856
+ order: 0,
45857
+ validators: [Validators.required],
45858
+ errors: { required: this.t('codeRequired') },
45859
+ state: ComponentStates.ENABLED,
45860
+ },
45861
+ ],
45862
+ },
45863
+ ],
45864
+ actions: this.submitBtn('delete-code-submit', 'confirmCta'),
45865
+ }));
45866
+ if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$3)) {
45867
+ this.i18n.registerContent(DEFAULT_NAMESPACE$3, DELETE_ACCOUNT_MODAL_I18N);
45355
45868
  }
45356
- const fence = line.match(FENCE_RE);
45357
- if (fence) {
45358
- const lang = fence[1] || undefined;
45359
- const body = [];
45360
- i++;
45361
- while (i < lines.length && !FENCE_RE.test(lines[i])) {
45362
- body.push(lines[i]);
45363
- i++;
45364
- }
45365
- i++;
45366
- elements.push({
45367
- type: 'code',
45368
- props: { code: body.join('\n'), language: lang, theme: 'dark' },
45869
+ }
45870
+ ngOnInit() {
45871
+ firstValueFrom(this.auth.getProfile())
45872
+ .then(profile => this.hasPassword.set(profile.hasPassword ?? true))
45873
+ .catch(() => this.hasPassword.set(true));
45874
+ }
45875
+ submitBtn(token, textKey) {
45876
+ return {
45877
+ text: this.t(textKey),
45878
+ type: 'submit',
45879
+ expand: 'block',
45880
+ fill: 'solid',
45881
+ color: 'dark',
45882
+ size: 'default',
45883
+ token,
45884
+ state: ComponentStates.ENABLED,
45885
+ };
45886
+ }
45887
+ close() {
45888
+ this._modalRef?.dismiss(undefined, 'cancel');
45889
+ }
45890
+ async onPasswordSubmit(event) {
45891
+ const password = event.fields['password'];
45892
+ if (!password) {
45893
+ this.toast.show({
45894
+ message: this.t('passwordRequired'),
45895
+ duration: 3000,
45896
+ color: 'dark',
45897
+ position: 'top',
45369
45898
  });
45370
- continue;
45899
+ return;
45371
45900
  }
45372
- if (BOX_DRAWING.test(line)) {
45373
- const body = [];
45374
- while (i < lines.length && (lines[i].trim() === '' || BOX_DRAWING.test(lines[i]))) {
45375
- body.push(lines[i]);
45376
- i++;
45377
- }
45378
- while (body.length && body[body.length - 1].trim() === '')
45379
- body.pop();
45380
- elements.push({
45381
- type: 'code',
45382
- props: { code: body.join('\n'), language: 'text', theme: 'dark' },
45383
- });
45384
- continue;
45901
+ this.formState.set(ComponentStates.WORKING);
45902
+ try {
45903
+ await firstValueFrom(this.auth.deleteAccount({ password }));
45385
45904
  }
45386
- if (SEPARATOR_RE.test(line)) {
45387
- elements.push({ type: 'separator', props: { style: 'line' } });
45388
- i++;
45389
- continue;
45905
+ catch (err) {
45906
+ this.errors.handle(err, { context: 'delete-account.password' });
45907
+ this.formState.set(ComponentStates.ENABLED);
45390
45908
  }
45391
- const heading = line.match(HEADING_RE);
45392
- if (heading) {
45393
- elements.push(makeHeading(heading[1].length, heading[2].trim()));
45394
- i++;
45395
- continue;
45909
+ }
45910
+ async onSendCode() {
45911
+ this.formState.set(ComponentStates.WORKING);
45912
+ try {
45913
+ await firstValueFrom(this.auth.sendDeleteAccountCode());
45914
+ this.codeSent.set(true);
45396
45915
  }
45397
- if (line.startsWith('>')) {
45398
- const block = [];
45399
- while (i < lines.length && lines[i].startsWith('>')) {
45400
- block.push(lines[i]);
45401
- i++;
45402
- }
45403
- elements.push(makeQuoteOrCallout(block, labels));
45404
- continue;
45916
+ catch (err) {
45917
+ this.errors.handle(err, { context: 'delete-account.send-code' });
45405
45918
  }
45406
- if (TABLE_ROW_RE.test(line)) {
45407
- const rows = [];
45408
- while (i < lines.length && TABLE_ROW_RE.test(lines[i])) {
45409
- rows.push(lines[i]);
45410
- i++;
45411
- }
45412
- elements.push(...makeTable(rows));
45413
- continue;
45919
+ finally {
45920
+ this.formState.set(ComponentStates.ENABLED);
45414
45921
  }
45415
- if (CHECKLIST_RE.test(line) || UNORDERED_RE.test(line) || ORDERED_RE.test(line)) {
45416
- const listType = CHECKLIST_RE.test(line)
45417
- ? 'checklist'
45418
- : ORDERED_RE.test(line)
45419
- ? 'ordered'
45420
- : 'unordered';
45421
- const items = [];
45422
- while (i < lines.length && lines[i].trim() !== '') {
45423
- const cur = lines[i];
45424
- const check = cur.match(CHECKLIST_RE);
45425
- const unord = cur.match(UNORDERED_RE);
45426
- const ord = cur.match(ORDERED_RE);
45427
- if (check)
45428
- items.push({ text: processInlineBold(check[2].trim()) });
45429
- else if (ord && listType === 'ordered')
45430
- items.push({ text: processInlineBold(ord[1].trim()) });
45431
- else if (unord && listType !== 'ordered')
45432
- items.push({ text: processInlineBold(unord[1].trim()) });
45433
- else
45434
- break;
45435
- i++;
45436
- }
45437
- elements.push({ type: 'list', props: { items, listType } });
45438
- continue;
45922
+ }
45923
+ async onResendCode() {
45924
+ this.codeSent.set(false);
45925
+ await this.onSendCode();
45926
+ }
45927
+ async onCodeSubmit(event) {
45928
+ const code = event.fields['code'];
45929
+ if (!code) {
45930
+ this.toast.show({
45931
+ message: this.t('codeRequired'),
45932
+ duration: 3000,
45933
+ color: 'dark',
45934
+ position: 'top',
45935
+ });
45936
+ return;
45439
45937
  }
45440
- const paragraph = [];
45441
- while (i < lines.length && lines[i].trim() !== '' && !startsNewBlock(lines[i])) {
45442
- paragraph.push(lines[i]);
45443
- i++;
45938
+ this.formState.set(ComponentStates.WORKING);
45939
+ try {
45940
+ await firstValueFrom(this.auth.deleteAccount({ code }));
45444
45941
  }
45445
- const text = processInlineBold(paragraph.join(' ').trim());
45446
- if (text) {
45447
- elements.push({
45448
- type: 'paragraph',
45449
- props: {
45450
- content: text,
45451
- size: 'medium',
45452
- color: 'dark',
45453
- bold: false,
45454
- processLinks: true,
45455
- allowPartialBold: true,
45456
- },
45457
- });
45942
+ catch (err) {
45943
+ this.errors.handle(err, { context: 'delete-account.code' });
45944
+ this.formState.set(ComponentStates.ENABLED);
45458
45945
  }
45459
45946
  }
45460
- // Strip parser-only options before merging into ArticleMetadata
45461
- const { locale: _l, calloutLabels: _c, ...metadataOverrides } = options ?? {};
45947
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DeleteAccountModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
45948
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: DeleteAccountModalComponent, isStandalone: true, selector: "val-delete-account-modal", inputs: { _modalRef: "_modalRef", i18nNamespace: "i18nNamespace" }, ngImport: i0, template: `
45949
+ <ion-header>
45950
+ <ion-toolbar>
45951
+ <ion-buttons slot="end">
45952
+ <ion-button fill="clear" color="dark" (click)="close()">
45953
+ <strong>{{ t('close') }}</strong>
45954
+ </ion-button>
45955
+ </ion-buttons>
45956
+ </ion-toolbar>
45957
+ </ion-header>
45958
+
45959
+ <ion-content class="ion-padding">
45960
+ <section class="modal-form-section">
45961
+ @if (hasPassword() === null) {
45962
+ <div class="spinner-center">
45963
+ <ion-spinner name="crescent" />
45964
+ </div>
45965
+ } @else if (hasPassword()) {
45966
+ <val-form [props]="passwordFormProps()" (onSubmit)="onPasswordSubmit($event)" />
45967
+ } @else if (!codeSent()) {
45968
+ <val-form [props]="sendCodeFormProps()" (onSubmit)="onSendCode()" />
45969
+ } @else {
45970
+ <val-form [props]="codeFormProps()" (onSubmit)="onCodeSubmit($event)" />
45971
+ <div class="resend-row">
45972
+ <ion-button
45973
+ fill="clear"
45974
+ color="medium"
45975
+ size="small"
45976
+ [disabled]="formState() === 'WORKING'"
45977
+ (click)="onResendCode()"
45978
+ >
45979
+ {{ t('resend') }}
45980
+ </ion-button>
45981
+ </div>
45982
+ }
45983
+ </section>
45984
+ </ion-content>
45985
+ `, isInline: true, styles: [".modal-form-section{display:flex;flex-direction:column;gap:16px;max-width:420px;margin:0 auto;padding-top:8px}.resend-row{display:flex;justify-content:center;margin-top:-8px}.spinner-center{display:flex;justify-content:center;padding-top:32px}\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"] }] }); }
45986
+ }
45987
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DeleteAccountModalComponent, decorators: [{
45988
+ type: Component,
45989
+ args: [{ selector: 'val-delete-account-modal', standalone: true, imports: [IonHeader, IonToolbar, IonButtons, IonButton, IonContent, IonSpinner, FormComponent], template: `
45990
+ <ion-header>
45991
+ <ion-toolbar>
45992
+ <ion-buttons slot="end">
45993
+ <ion-button fill="clear" color="dark" (click)="close()">
45994
+ <strong>{{ t('close') }}</strong>
45995
+ </ion-button>
45996
+ </ion-buttons>
45997
+ </ion-toolbar>
45998
+ </ion-header>
45999
+
46000
+ <ion-content class="ion-padding">
46001
+ <section class="modal-form-section">
46002
+ @if (hasPassword() === null) {
46003
+ <div class="spinner-center">
46004
+ <ion-spinner name="crescent" />
46005
+ </div>
46006
+ } @else if (hasPassword()) {
46007
+ <val-form [props]="passwordFormProps()" (onSubmit)="onPasswordSubmit($event)" />
46008
+ } @else if (!codeSent()) {
46009
+ <val-form [props]="sendCodeFormProps()" (onSubmit)="onSendCode()" />
46010
+ } @else {
46011
+ <val-form [props]="codeFormProps()" (onSubmit)="onCodeSubmit($event)" />
46012
+ <div class="resend-row">
46013
+ <ion-button
46014
+ fill="clear"
46015
+ color="medium"
46016
+ size="small"
46017
+ [disabled]="formState() === 'WORKING'"
46018
+ (click)="onResendCode()"
46019
+ >
46020
+ {{ t('resend') }}
46021
+ </ion-button>
46022
+ </div>
46023
+ }
46024
+ </section>
46025
+ </ion-content>
46026
+ `, styles: [".modal-form-section{display:flex;flex-direction:column;gap:16px;max-width:420px;margin:0 auto;padding-top:8px}.resend-row{display:flex;justify-content:center;margin-top:-8px}.spinner-center{display:flex;justify-content:center;padding-top:32px}\n"] }]
46027
+ }], ctorParameters: () => [], propDecorators: { _modalRef: [{
46028
+ type: Input
46029
+ }], i18nNamespace: [{
46030
+ type: Input
46031
+ }] } });
46032
+
46033
+ /**
46034
+ * Defaults i18n (es/en) embebidos en `val-org-info-sheet`. Auto-registrados en el
46035
+ * constructor del componente si el consumer no proveyó el namespace
46036
+ * (`OrgInfoSheet`). Cubre los textos de los 3 features (que en la versión inline
46037
+ * de showcase estaban hardcoded) más fallbacks de título/subtítulo/closeLabel.
46038
+ *
46039
+ * `title`/`subtitle`/`body`/`closeLabel` los suele pasar el consumer ya resueltos
46040
+ * por props (patrón del resto de modales); las keys `feature*` se auto-resuelven
46041
+ * desde este namespace.
46042
+ */
46043
+ const ORG_INFO_SHEET_I18N = {
46044
+ es: {
46045
+ title: '¿Qué son las organizaciones?',
46046
+ subtitle: 'Todo lo que necesitas saber',
46047
+ close: 'Cerrar',
46048
+ featureCollabTitle: 'Colaboración',
46049
+ featureCollabDesc: 'Separa proyectos y datos por empresa o equipo.',
46050
+ featureSwitchTitle: 'Cambio rápido',
46051
+ featureSwitchDesc: 'Cambia entre organizaciones en cualquier momento.',
46052
+ featureInviteTitle: 'Invitaciones',
46053
+ featureInviteDesc: 'Invita a otros a unirse mediante email.',
46054
+ },
46055
+ en: {
46056
+ title: 'What are organizations?',
46057
+ subtitle: 'Everything you need to know',
46058
+ close: 'Close',
46059
+ featureCollabTitle: 'Collaboration',
46060
+ featureCollabDesc: 'Separate projects and data by company or team.',
46061
+ featureSwitchTitle: 'Quick switch',
46062
+ featureSwitchDesc: 'Switch between organizations at any time.',
46063
+ featureInviteTitle: 'Invitations',
46064
+ featureInviteDesc: 'Invite others to join via email.',
46065
+ },
46066
+ };
46067
+
46068
+ const DEFAULT_NAMESPACE$2 = 'OrgInfoSheet';
46069
+ /**
46070
+ * `val-org-info-sheet` — sheet informativo "¿Qué son las organizaciones?".
46071
+ * Extraído del `OrgInfoSheetComponent` inline de `AccountPage` bajo el proceso de
46072
+ * ADR-021.
46073
+ *
46074
+ * El consumer pasa `title`/`subtitle`/`body`/`closeLabel` ya resueltos por props
46075
+ * (patrón del resto de modales del factory). Los textos de los 3 features
46076
+ * (Colaboración / Cambio rápido / Invitaciones), que en la versión inline estaban
46077
+ * hardcoded, ahora se auto-resuelven desde el namespace `OrgInfoSheet`
46078
+ * (auto-registrado en el constructor).
46079
+ *
46080
+ * Abierto vía `ModalService` — recibe `_modalRef` por `componentProps`. Header
46081
+ * canónico (Regla #5): sin `ion-title`, botón Cerrar texto en `slot=end`,
46082
+ * título/subtítulo en el body con `val-display`/`val-title`.
46083
+ */
46084
+ class OrgInfoSheetComponent {
46085
+ constructor() {
46086
+ this.i18n = inject(I18nService);
46087
+ /** Título principal (resuelto por el consumer). Fallback al namespace propio. */
46088
+ this.title = '';
46089
+ /** Subtítulo (resuelto por el consumer). Fallback al namespace propio. */
46090
+ this.subtitle = '';
46091
+ /** Cuerpo en párrafos separados por doble salto de línea (resuelto por el consumer). */
46092
+ this.body = '';
46093
+ /** Label del botón cerrar (resuelto por el consumer). Fallback al namespace propio. */
46094
+ this.closeLabel = '';
46095
+ /** Props del botón cerrar del footer (el consumer puede pasar custom). */
46096
+ this.closeProps = {
46097
+ text: 'Cerrar',
46098
+ fill: 'outline',
46099
+ color: 'dark',
46100
+ size: 'default',
46101
+ expand: 'block',
46102
+ type: 'button',
46103
+ };
46104
+ this.resolvedTitle = computed(() => this.title || this.tt('title'));
46105
+ this.resolvedSubtitle = computed(() => this.subtitle || this.tt('subtitle'));
46106
+ this.resolvedCloseLabel = computed(() => this.closeLabel || this.tt('close'));
46107
+ this.paragraphs = computed(() => this.body.split('\n\n').filter(Boolean));
46108
+ this.resolvedCloseProps = computed(() => ({
46109
+ ...this.closeProps,
46110
+ text: this.closeProps.text || this.resolvedCloseLabel(),
46111
+ }));
46112
+ addIcons({ peopleOutline, swapHorizontalOutline, mailOutline });
46113
+ if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$2)) {
46114
+ this.i18n.registerContent(DEFAULT_NAMESPACE$2, ORG_INFO_SHEET_I18N);
46115
+ }
46116
+ }
46117
+ dismiss() {
46118
+ this._modalRef?.dismiss(null, 'cancel');
46119
+ }
46120
+ tt(key) {
46121
+ return this.i18n.t(key, DEFAULT_NAMESPACE$2);
46122
+ }
46123
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: OrgInfoSheetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
46124
+ 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: `
46125
+ <ion-header>
46126
+ <ion-toolbar>
46127
+ <ion-buttons slot="end">
46128
+ <ion-button fill="clear" color="dark" (click)="dismiss()">
46129
+ <strong>{{ resolvedCloseLabel() }}</strong>
46130
+ </ion-button>
46131
+ </ion-buttons>
46132
+ </ion-toolbar>
46133
+ </ion-header>
46134
+
46135
+ <ion-content class="ion-padding">
46136
+ <val-display [props]="{ content: resolvedTitle(), size: 'small', color: 'dark' }" />
46137
+ <val-title [props]="{ content: resolvedSubtitle(), size: 'large', color: '', bold: false }" />
46138
+
46139
+ <div class="info-features">
46140
+ <div class="info-feature">
46141
+ <ion-icon name="people-outline" class="info-feature__icon" />
46142
+ <div>
46143
+ <div class="info-feature__title">{{ tt('featureCollabTitle') }}</div>
46144
+ <div class="info-feature__desc">{{ tt('featureCollabDesc') }}</div>
46145
+ </div>
46146
+ </div>
46147
+ <div class="info-feature">
46148
+ <ion-icon name="swap-horizontal-outline" class="info-feature__icon" />
46149
+ <div>
46150
+ <div class="info-feature__title">{{ tt('featureSwitchTitle') }}</div>
46151
+ <div class="info-feature__desc">{{ tt('featureSwitchDesc') }}</div>
46152
+ </div>
46153
+ </div>
46154
+ <div class="info-feature">
46155
+ <ion-icon name="mail-outline" class="info-feature__icon" />
46156
+ <div>
46157
+ <div class="info-feature__title">{{ tt('featureInviteTitle') }}</div>
46158
+ <div class="info-feature__desc">{{ tt('featureInviteDesc') }}</div>
46159
+ </div>
46160
+ </div>
46161
+ </div>
46162
+
46163
+ @for (para of paragraphs(); track $index) {
46164
+ <val-text
46165
+ [props]="{
46166
+ size: 'medium',
46167
+ color: 'dark',
46168
+ bold: false,
46169
+ content: para,
46170
+ }"
46171
+ />
46172
+ }
46173
+
46174
+ <div class="sheet-footer">
46175
+ <val-button [props]="resolvedCloseProps()" (click)="dismiss()" />
46176
+ </div>
46177
+ </ion-content>
46178
+ `, isInline: true, styles: ["ion-content{--padding-bottom: 32px}val-display{margin-bottom:4px;display:block}val-title{margin-bottom:8px;display:block}.info-features{display:flex;flex-direction:column;gap:12px;margin:16px 0}.info-feature{display:flex;align-items:flex-start;gap:14px;padding:12px 14px;border-radius:10px;background:var(--ion-color-light, #f4f5f8)}:host-context(body.dark) .info-feature,:host-context(html.ion-palette-dark) .info-feature,:host-context([data-theme=\"dark\"]) .info-feature{background:#ffffff0d}.info-feature__icon{font-size:22px;color:var(--ion-color-primary);flex-shrink:0;margin-top:2px}.info-feature__title{font-weight:600;font-size:.9rem;color:var(--ion-color-dark)}.info-feature__desc{font-size:.82rem;color:var(--ion-color-medium);margin-top:2px}.sheet-footer{margin-top:20px}\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: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: ButtonComponent, selector: "val-button", inputs: ["preset", "props"], outputs: ["onClick"] }, { 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"] }] }); }
46179
+ }
46180
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: OrgInfoSheetComponent, decorators: [{
46181
+ type: Component,
46182
+ args: [{ selector: 'val-org-info-sheet', standalone: true, imports: [
46183
+ IonHeader,
46184
+ IonToolbar,
46185
+ IonButtons,
46186
+ IonButton,
46187
+ IonContent,
46188
+ IonIcon,
46189
+ ButtonComponent,
46190
+ DisplayComponent,
46191
+ TextComponent,
46192
+ TitleComponent,
46193
+ ], template: `
46194
+ <ion-header>
46195
+ <ion-toolbar>
46196
+ <ion-buttons slot="end">
46197
+ <ion-button fill="clear" color="dark" (click)="dismiss()">
46198
+ <strong>{{ resolvedCloseLabel() }}</strong>
46199
+ </ion-button>
46200
+ </ion-buttons>
46201
+ </ion-toolbar>
46202
+ </ion-header>
46203
+
46204
+ <ion-content class="ion-padding">
46205
+ <val-display [props]="{ content: resolvedTitle(), size: 'small', color: 'dark' }" />
46206
+ <val-title [props]="{ content: resolvedSubtitle(), size: 'large', color: '', bold: false }" />
46207
+
46208
+ <div class="info-features">
46209
+ <div class="info-feature">
46210
+ <ion-icon name="people-outline" class="info-feature__icon" />
46211
+ <div>
46212
+ <div class="info-feature__title">{{ tt('featureCollabTitle') }}</div>
46213
+ <div class="info-feature__desc">{{ tt('featureCollabDesc') }}</div>
46214
+ </div>
46215
+ </div>
46216
+ <div class="info-feature">
46217
+ <ion-icon name="swap-horizontal-outline" class="info-feature__icon" />
46218
+ <div>
46219
+ <div class="info-feature__title">{{ tt('featureSwitchTitle') }}</div>
46220
+ <div class="info-feature__desc">{{ tt('featureSwitchDesc') }}</div>
46221
+ </div>
46222
+ </div>
46223
+ <div class="info-feature">
46224
+ <ion-icon name="mail-outline" class="info-feature__icon" />
46225
+ <div>
46226
+ <div class="info-feature__title">{{ tt('featureInviteTitle') }}</div>
46227
+ <div class="info-feature__desc">{{ tt('featureInviteDesc') }}</div>
46228
+ </div>
46229
+ </div>
46230
+ </div>
46231
+
46232
+ @for (para of paragraphs(); track $index) {
46233
+ <val-text
46234
+ [props]="{
46235
+ size: 'medium',
46236
+ color: 'dark',
46237
+ bold: false,
46238
+ content: para,
46239
+ }"
46240
+ />
46241
+ }
46242
+
46243
+ <div class="sheet-footer">
46244
+ <val-button [props]="resolvedCloseProps()" (click)="dismiss()" />
46245
+ </div>
46246
+ </ion-content>
46247
+ `, styles: ["ion-content{--padding-bottom: 32px}val-display{margin-bottom:4px;display:block}val-title{margin-bottom:8px;display:block}.info-features{display:flex;flex-direction:column;gap:12px;margin:16px 0}.info-feature{display:flex;align-items:flex-start;gap:14px;padding:12px 14px;border-radius:10px;background:var(--ion-color-light, #f4f5f8)}:host-context(body.dark) .info-feature,:host-context(html.ion-palette-dark) .info-feature,:host-context([data-theme=\"dark\"]) .info-feature{background:#ffffff0d}.info-feature__icon{font-size:22px;color:var(--ion-color-primary);flex-shrink:0;margin-top:2px}.info-feature__title{font-weight:600;font-size:.9rem;color:var(--ion-color-dark)}.info-feature__desc{font-size:.82rem;color:var(--ion-color-medium);margin-top:2px}.sheet-footer{margin-top:20px}\n"] }]
46248
+ }], ctorParameters: () => [], propDecorators: { title: [{
46249
+ type: Input
46250
+ }], subtitle: [{
46251
+ type: Input
46252
+ }], body: [{
46253
+ type: Input
46254
+ }], closeLabel: [{
46255
+ type: Input
46256
+ }], closeProps: [{
46257
+ type: Input
46258
+ }], _modalRef: [{
46259
+ type: Input
46260
+ }] } });
46261
+
46262
+ /**
46263
+ * Defaults i18n (es/en) embebidos en `val-switch-org-modal`. Auto-registrados en
46264
+ * el constructor del componente si el consumer no proveyó el namespace
46265
+ * (`Settings.SwitchOrg` por default). SOLO incluye las keys que el modal usa.
46266
+ */
46267
+ const SWITCH_ORG_MODAL_I18N = {
46268
+ es: {
46269
+ title: 'Cambiar de cuenta',
46270
+ subtitle: 'Selecciona la organización a la que quieres cambiar.',
46271
+ searchPlaceholder: 'Buscar organización...',
46272
+ noResults: 'No se encontraron organizaciones.',
46273
+ current: 'Actual',
46274
+ close: 'Cerrar',
46275
+ switchError: 'No se pudo cambiar de organización.',
46276
+ },
46277
+ en: {
46278
+ title: 'Switch account',
46279
+ subtitle: 'Select the organization you want to switch to.',
46280
+ searchPlaceholder: 'Search organization...',
46281
+ noResults: 'No organizations found.',
46282
+ current: 'Active',
46283
+ close: 'Close',
46284
+ switchError: 'Could not switch organization.',
46285
+ },
46286
+ };
46287
+
46288
+ const DEFAULT_NAMESPACE$1 = 'Settings.SwitchOrg';
46289
+ addIcons({ businessOutline, checkmarkCircleOutline });
46290
+ /**
46291
+ * `val-switch-org-modal` — modal de cambio de organización activa (organism
46292
+ * compartido). Promovido desde `showcase` bajo el proceso de ADR-021. Reusado por
46293
+ * la vista Cuenta (y futuras vistas org-aware).
46294
+ *
46295
+ * Lista las organizaciones del user (vía `OrgService.listMyOrgs`), permite buscar
46296
+ * por nombre, y cambia la org activa con `OrgSwitchService.switchTo`. La org
46297
+ * actual queda primera y deshabilitada.
46298
+ *
46299
+ * Abierto vía `ModalService` — recibe `_modalRef` por `componentProps` + callback
46300
+ * `onSuccess`. Header canónico (Regla #5): sin `ion-title`, botón Cerrar texto en
46301
+ * `slot=end`, título/subtítulo en el body con `val-display`/`val-title`.
46302
+ *
46303
+ * Auto-registra sus defaults i18n (es/en) si el consumer no proveyó el namespace
46304
+ * `Settings.SwitchOrg`.
46305
+ */
46306
+ class SwitchOrgModalComponent {
46307
+ constructor() {
46308
+ this.i18n = inject(I18nService);
46309
+ this.auth = inject(AuthService);
46310
+ this.orgService = inject(OrgService);
46311
+ this.orgSwitch = inject(OrgSwitchService);
46312
+ this.errors = inject(ValtechErrorService);
46313
+ /** Namespace i18n con que la vista resuelve sus textos. */
46314
+ this.i18nNamespace = DEFAULT_NAMESPACE$1;
46315
+ this.orgs = signal([]);
46316
+ this.loading = signal(false);
46317
+ this.query = signal('');
46318
+ this.switchingId = signal(null);
46319
+ this.activeOrgId = computed(() => {
46320
+ const u = this.auth.user();
46321
+ return u?.activeOrgId ?? u?.activeOrg ?? '';
46322
+ });
46323
+ this.filteredOrgs = computed(() => {
46324
+ const q = this.query().trim().toLowerCase();
46325
+ const list = this.orgs();
46326
+ if (!q)
46327
+ return list;
46328
+ return list.filter(o => o.name.toLowerCase().includes(q));
46329
+ });
46330
+ if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$1)) {
46331
+ this.i18n.registerContent(DEFAULT_NAMESPACE$1, SWITCH_ORG_MODAL_I18N);
46332
+ }
46333
+ this.loadOrgs();
46334
+ }
46335
+ onQueryChange(value) {
46336
+ this.query.set(value ?? '');
46337
+ }
46338
+ async onSelect(org) {
46339
+ if (this.switchingId() || org.id === this.activeOrgId())
46340
+ return;
46341
+ this.switchingId.set(org.id);
46342
+ try {
46343
+ await this.orgSwitch.switchTo(org.id);
46344
+ this.onSuccess?.();
46345
+ this.dismiss();
46346
+ }
46347
+ catch (err) {
46348
+ this.errors.handle(err, {
46349
+ context: 'account.switchOrg',
46350
+ fallbackKey: 'switchError',
46351
+ i18nNamespace: this.i18nNamespace,
46352
+ });
46353
+ this.switchingId.set(null);
46354
+ }
46355
+ }
46356
+ loadOrgs() {
46357
+ this.loading.set(true);
46358
+ this.orgService
46359
+ .listMyOrgs()
46360
+ .pipe(take(1))
46361
+ .subscribe({
46362
+ next: orgs => {
46363
+ const activeId = this.activeOrgId();
46364
+ this.orgs.set([...orgs].sort((a, b) => (a.id === activeId ? -1 : b.id === activeId ? 1 : 0)));
46365
+ this.loading.set(false);
46366
+ },
46367
+ error: () => this.loading.set(false),
46368
+ });
46369
+ }
46370
+ dismiss() {
46371
+ this._modalRef?.dismiss(null, 'cancel');
46372
+ }
46373
+ t(key) {
46374
+ return this.i18n.t(key, this.i18nNamespace);
46375
+ }
46376
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SwitchOrgModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
46377
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: SwitchOrgModalComponent, isStandalone: true, selector: "val-switch-org-modal", inputs: { _modalRef: "_modalRef", onSuccess: "onSuccess", i18nNamespace: "i18nNamespace" }, ngImport: i0, template: `
46378
+ <ion-header>
46379
+ <ion-toolbar>
46380
+ <ion-buttons slot="end">
46381
+ <ion-button fill="clear" color="dark" (click)="dismiss()">
46382
+ <strong>{{ t('close') }}</strong>
46383
+ </ion-button>
46384
+ </ion-buttons>
46385
+ </ion-toolbar>
46386
+ </ion-header>
46387
+
46388
+ <ion-content class="ion-padding">
46389
+ <val-display [props]="{ content: t('title'), size: 'small', color: 'dark' }" />
46390
+ <val-title
46391
+ [props]="{
46392
+ content: t('subtitle'),
46393
+ size: 'large',
46394
+ color: '',
46395
+ bold: false,
46396
+ }"
46397
+ />
46398
+
46399
+ <div class="switch-section">
46400
+ <val-searchbar
46401
+ [props]="{
46402
+ placeholder: t('searchPlaceholder'),
46403
+ debounce: 200,
46404
+ showCancelButton: 'never',
46405
+ }"
46406
+ (filterEvent)="onQueryChange($event)"
46407
+ />
46408
+
46409
+ @if (loading()) {
46410
+ <div class="switch-state">
46411
+ <ion-spinner name="crescent" />
46412
+ </div>
46413
+ } @else if (filteredOrgs().length === 0) {
46414
+ <val-empty-state
46415
+ [props]="{
46416
+ variant: 'empty',
46417
+ title: t('noResults'),
46418
+ icon: 'business-outline',
46419
+ }"
46420
+ />
46421
+ } @else {
46422
+ <div class="orgs-list">
46423
+ @for (org of filteredOrgs(); track org.id) {
46424
+ <button
46425
+ type="button"
46426
+ class="org-row"
46427
+ [class.org-row--active]="org.id === activeOrgId()"
46428
+ [disabled]="!!switchingId() || org.id === activeOrgId()"
46429
+ (click)="onSelect(org)"
46430
+ >
46431
+ <div class="org-row__icon">
46432
+ <ion-icon name="business-outline" />
46433
+ </div>
46434
+ <div class="org-row__body">
46435
+ <span class="org-row__name">{{ org.name }}</span>
46436
+ <span class="org-row__type">{{ org.type }}</span>
46437
+ </div>
46438
+ <div class="org-row__end">
46439
+ @if (switchingId() === org.id) {
46440
+ <ion-spinner name="crescent" class="org-row__spinner" />
46441
+ } @else if (org.id === activeOrgId()) {
46442
+ <span class="org-row__current">{{ t('current') }}</span>
46443
+ <ion-icon name="checkmark-circle-outline" class="org-row__check" />
46444
+ }
46445
+ </div>
46446
+ </button>
46447
+ }
46448
+ </div>
46449
+ }
46450
+ </div>
46451
+ </ion-content>
46452
+ `, isInline: true, styles: [".switch-section{margin-top:16px;display:flex;flex-direction:column;gap:12px}.switch-state{display:flex;justify-content:center;padding:24px 0}.orgs-list{display:flex;flex-direction:column;gap:8px}.org-row{display:flex;align-items:center;gap:12px;width:100%;padding:12px 14px;border:1px solid rgba(var(--ion-color-dark-rgb),.14);border-radius:12px;background:var(--ion-card-background, var(--ion-background-color));cursor:pointer;text-align:left;transition:border-color .15s ease,opacity .15s ease}.org-row:not(:disabled):active{opacity:.7}.org-row:disabled{cursor:default}.org-row--active{border-color:var(--ion-color-dark)}.org-row__icon{width:36px;height:36px;border-radius:50%;flex-shrink:0;display:flex;align-items:center;justify-content:center;font-size:18px;color:var(--ion-color-dark);background:rgba(var(--ion-color-dark-rgb),.08)}.org-row__body{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}.org-row__name{font-weight:600;font-size:.95rem;color:var(--ion-color-dark);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.org-row__type{font-size:.8rem;color:rgba(var(--ion-color-dark-rgb),.7)}.org-row__end{display:flex;align-items:center;gap:6px;flex-shrink:0}.org-row__current{font-size:.74rem;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:rgba(var(--ion-color-dark-rgb),.7)}.org-row__check{font-size:1.1rem;color:var(--ion-color-dark)}.org-row__spinner{width:18px;height:18px;color:var(--ion-color-dark)}\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: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: DisplayComponent, selector: "val-display", inputs: ["props"] }, { kind: "component", type: EmptyStateComponent, selector: "val-empty-state", inputs: ["props"] }, { kind: "component", type: SearchbarComponent, selector: "val-searchbar", inputs: ["preset", "props"], outputs: ["filterEvent", "focusEvent", "blurEvent"] }, { kind: "component", type: TitleComponent, selector: "val-title", inputs: ["props"] }] }); }
46453
+ }
46454
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SwitchOrgModalComponent, decorators: [{
46455
+ type: Component,
46456
+ args: [{ selector: 'val-switch-org-modal', standalone: true, imports: [
46457
+ IonHeader,
46458
+ IonToolbar,
46459
+ IonButtons,
46460
+ IonButton,
46461
+ IonContent,
46462
+ IonSpinner,
46463
+ IonIcon,
46464
+ DisplayComponent,
46465
+ EmptyStateComponent,
46466
+ SearchbarComponent,
46467
+ TitleComponent,
46468
+ ], template: `
46469
+ <ion-header>
46470
+ <ion-toolbar>
46471
+ <ion-buttons slot="end">
46472
+ <ion-button fill="clear" color="dark" (click)="dismiss()">
46473
+ <strong>{{ t('close') }}</strong>
46474
+ </ion-button>
46475
+ </ion-buttons>
46476
+ </ion-toolbar>
46477
+ </ion-header>
46478
+
46479
+ <ion-content class="ion-padding">
46480
+ <val-display [props]="{ content: t('title'), size: 'small', color: 'dark' }" />
46481
+ <val-title
46482
+ [props]="{
46483
+ content: t('subtitle'),
46484
+ size: 'large',
46485
+ color: '',
46486
+ bold: false,
46487
+ }"
46488
+ />
46489
+
46490
+ <div class="switch-section">
46491
+ <val-searchbar
46492
+ [props]="{
46493
+ placeholder: t('searchPlaceholder'),
46494
+ debounce: 200,
46495
+ showCancelButton: 'never',
46496
+ }"
46497
+ (filterEvent)="onQueryChange($event)"
46498
+ />
46499
+
46500
+ @if (loading()) {
46501
+ <div class="switch-state">
46502
+ <ion-spinner name="crescent" />
46503
+ </div>
46504
+ } @else if (filteredOrgs().length === 0) {
46505
+ <val-empty-state
46506
+ [props]="{
46507
+ variant: 'empty',
46508
+ title: t('noResults'),
46509
+ icon: 'business-outline',
46510
+ }"
46511
+ />
46512
+ } @else {
46513
+ <div class="orgs-list">
46514
+ @for (org of filteredOrgs(); track org.id) {
46515
+ <button
46516
+ type="button"
46517
+ class="org-row"
46518
+ [class.org-row--active]="org.id === activeOrgId()"
46519
+ [disabled]="!!switchingId() || org.id === activeOrgId()"
46520
+ (click)="onSelect(org)"
46521
+ >
46522
+ <div class="org-row__icon">
46523
+ <ion-icon name="business-outline" />
46524
+ </div>
46525
+ <div class="org-row__body">
46526
+ <span class="org-row__name">{{ org.name }}</span>
46527
+ <span class="org-row__type">{{ org.type }}</span>
46528
+ </div>
46529
+ <div class="org-row__end">
46530
+ @if (switchingId() === org.id) {
46531
+ <ion-spinner name="crescent" class="org-row__spinner" />
46532
+ } @else if (org.id === activeOrgId()) {
46533
+ <span class="org-row__current">{{ t('current') }}</span>
46534
+ <ion-icon name="checkmark-circle-outline" class="org-row__check" />
46535
+ }
46536
+ </div>
46537
+ </button>
46538
+ }
46539
+ </div>
46540
+ }
46541
+ </div>
46542
+ </ion-content>
46543
+ `, styles: [".switch-section{margin-top:16px;display:flex;flex-direction:column;gap:12px}.switch-state{display:flex;justify-content:center;padding:24px 0}.orgs-list{display:flex;flex-direction:column;gap:8px}.org-row{display:flex;align-items:center;gap:12px;width:100%;padding:12px 14px;border:1px solid rgba(var(--ion-color-dark-rgb),.14);border-radius:12px;background:var(--ion-card-background, var(--ion-background-color));cursor:pointer;text-align:left;transition:border-color .15s ease,opacity .15s ease}.org-row:not(:disabled):active{opacity:.7}.org-row:disabled{cursor:default}.org-row--active{border-color:var(--ion-color-dark)}.org-row__icon{width:36px;height:36px;border-radius:50%;flex-shrink:0;display:flex;align-items:center;justify-content:center;font-size:18px;color:var(--ion-color-dark);background:rgba(var(--ion-color-dark-rgb),.08)}.org-row__body{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}.org-row__name{font-weight:600;font-size:.95rem;color:var(--ion-color-dark);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.org-row__type{font-size:.8rem;color:rgba(var(--ion-color-dark-rgb),.7)}.org-row__end{display:flex;align-items:center;gap:6px;flex-shrink:0}.org-row__current{font-size:.74rem;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:rgba(var(--ion-color-dark-rgb),.7)}.org-row__check{font-size:1.1rem;color:var(--ion-color-dark)}.org-row__spinner{width:18px;height:18px;color:var(--ion-color-dark)}\n"] }]
46544
+ }], ctorParameters: () => [], propDecorators: { _modalRef: [{
46545
+ type: Input
46546
+ }], onSuccess: [{
46547
+ type: Input
46548
+ }], i18nNamespace: [{
46549
+ type: Input
46550
+ }] } });
46551
+
46552
+ /**
46553
+ * Defaults i18n (es/en) embebidos en `val-account-view`. Auto-registrados en el
46554
+ * constructor del componente si el consumer no proveyó el namespace
46555
+ * (`Settings.Account` por default). SOLO incluye las keys que la vista usa.
46556
+ */
46557
+ const ACCOUNT_VIEW_I18N = {
46558
+ es: {
46559
+ pageTitle: 'Cuenta',
46560
+ pageDescription: 'Organizaciones y seguridad de la cuenta',
46561
+ orgsTitle: 'Tus organizaciones',
46562
+ orgsCurrent: 'Actual',
46563
+ orgsEmpty: 'Aún no perteneces a otras organizaciones.',
46564
+ orgsSwitch: 'Cambiar',
46565
+ switchAccountCta: 'Cambiar de cuenta',
46566
+ orgsViewAll: 'Ver todas',
46567
+ orgsManage: 'Gestionar',
46568
+ orgsEmptyHint: 'Aún no perteneces a otras organizaciones.',
46569
+ orgsNewCta: 'Nueva organización',
46570
+ orgsNewQuestion: '¿Necesitas otra organización?',
46571
+ orgsNewHint: 'Crea un espacio separado para otro equipo, empresa o proyecto.',
46572
+ orgsMoreInfo: 'Más información',
46573
+ orgsInfoTitle: '¿Qué son las organizaciones?',
46574
+ orgsInfoSubtitle: 'Todo lo que necesitas saber',
46575
+ orgsInfoClose: 'Cerrar',
46576
+ orgsInfoContent: 'Las organizaciones te permiten separar tus proyectos y datos por empresa, equipo o contexto. Puedes pertenecer a varias organizaciones y cambiar entre ellas en cualquier momento.\n\nCada organización tiene sus propios miembros, roles y permisos. La organización activa determina qué datos ves y qué acciones puedes realizar.\n\nPuedes crear una organización nueva o unirte a una existente mediante invitación.',
46577
+ orgsTypePersonal: 'Personal',
46578
+ orgsTypePrivate: 'Privada',
46579
+ orgsTypePublic: 'Pública',
46580
+ orgsTypeBusiness: 'Empresa',
46581
+ orgsLabelType: 'Tipo',
46582
+ orgsLabelRole: 'Rol',
46583
+ orgsLabelStatus: 'Estado',
46584
+ orgsStatusActive: 'Activa',
46585
+ logoutTitle: 'Cerrar esta sesión',
46586
+ logoutHint: 'Cierra tu sesión en este dispositivo.',
46587
+ logout: 'Cerrar sesión',
46588
+ dangerTitle: 'Zona peligrosa',
46589
+ dangerDeleteTitle: 'Eliminar cuenta',
46590
+ dangerDeleteHint: 'Borra tu cuenta y todos los datos asociados. Esta acción no se puede deshacer.',
46591
+ dangerDeleteCta: 'Eliminar cuenta',
46592
+ inviteAccepted: 'Te has unido a la organización.',
46593
+ inviteDeclined: 'Invitación rechazada.',
46594
+ accept: 'Aceptar',
46595
+ decline: 'Rechazar',
46596
+ pendingInvitesTitle: 'Invitaciones pendientes',
46597
+ orgsErrorTitle: 'No se pudieron cargar las organizaciones',
46598
+ orgsOfflineTitle: 'Sin conexión',
46599
+ orgsOfflineHint: 'Verifica tu conexión e intenta de nuevo.',
46600
+ orgsRetry: 'Reintentar',
46601
+ },
46602
+ en: {
46603
+ pageTitle: 'Account',
46604
+ pageDescription: 'Organizations and account security',
46605
+ orgsTitle: 'Your organizations',
46606
+ orgsCurrent: 'Active',
46607
+ orgsEmpty: "You don't belong to other organizations yet.",
46608
+ orgsSwitch: 'Switch',
46609
+ switchAccountCta: 'Switch account',
46610
+ orgsViewAll: 'View all',
46611
+ orgsManage: 'Manage',
46612
+ orgsEmptyHint: "You don't belong to other organizations yet.",
46613
+ orgsNewCta: 'New organization',
46614
+ orgsNewQuestion: 'Need another organization?',
46615
+ orgsNewHint: 'Create a separate space for another team, company or project.',
46616
+ orgsMoreInfo: 'More information',
46617
+ orgsInfoTitle: 'What are organizations?',
46618
+ orgsInfoSubtitle: 'Everything you need to know',
46619
+ orgsInfoClose: 'Close',
46620
+ orgsInfoContent: 'Organizations let you separate your projects and data by company, team, or context. You can belong to multiple organizations and switch between them at any time.\n\nEach organization has its own members, roles, and permissions. The active organization determines what data you see and what actions you can take.\n\nYou can create a new organization or join an existing one via invitation.',
46621
+ orgsTypePersonal: 'Personal',
46622
+ orgsTypePrivate: 'Private',
46623
+ orgsTypePublic: 'Public',
46624
+ orgsTypeBusiness: 'Business',
46625
+ orgsLabelType: 'Type',
46626
+ orgsLabelRole: 'Role',
46627
+ orgsLabelStatus: 'Status',
46628
+ orgsStatusActive: 'Active',
46629
+ logoutTitle: 'Sign out of this device',
46630
+ logoutHint: 'End your session on this device.',
46631
+ logout: 'Sign out',
46632
+ dangerTitle: 'Danger zone',
46633
+ dangerDeleteTitle: 'Delete account',
46634
+ dangerDeleteHint: 'Delete your account and all related data. This action cannot be undone.',
46635
+ dangerDeleteCta: 'Delete account',
46636
+ inviteAccepted: 'You have joined the organization.',
46637
+ inviteDeclined: 'Invitation declined.',
46638
+ accept: 'Accept',
46639
+ decline: 'Decline',
46640
+ pendingInvitesTitle: 'Pending invitations',
46641
+ orgsErrorTitle: 'Could not load organizations',
46642
+ orgsOfflineTitle: 'No connection',
46643
+ orgsOfflineHint: 'Check your connection and try again.',
46644
+ orgsRetry: 'Retry',
46645
+ },
46646
+ };
46647
+
46648
+ addIcons({ peopleOutline, businessOutline, chevronForwardOutline, swapHorizontalOutline, mailOutline });
46649
+ const DEFAULT_NAMESPACE = 'Settings.Account';
46650
+ const DEFAULT_MANAGE_ORG_ROUTE = '/app/settings/organization';
46651
+ /**
46652
+ * `val-account-view` — vista Cuenta full-feature autocontenida (organism).
46653
+ * Promovida desde `showcase` bajo el proceso de ADR-021. Reúne:
46654
+ * - **Invitaciones pendientes** — aceptar / rechazar (`val-invitation-card`).
46655
+ * - **Organizaciones** — card de org activa + empty-card + CTA "Nueva org"
46656
+ * (modal `val-create-org-modal`) + "Ver todas" (modal `val-switch-org-modal`).
46657
+ * - **Cerrar esta sesión** — `auth.logoutWithConfirmation()`.
46658
+ * - **Eliminar cuenta** — modal `val-delete-account-modal`.
46659
+ *
46660
+ * NO renderiza ion-content — vive dentro de val-page-wrapper. No usa deep-links;
46661
+ * `ActivatedRoute` se inyecta `{ optional: true }` solo para leer el route data
46662
+ * de config. La navegación a la org route va por `NavigationService`.
46663
+ *
46664
+ * Auto-registra sus defaults i18n (es/en) en el constructor si el consumer no
46665
+ * proveyó el namespace configurado (default `Settings.Account`).
46666
+ */
46667
+ class AccountViewComponent {
46668
+ /** Namespace i18n resuelto (capturado para llamadas no-reactivas). */
46669
+ get ns() {
46670
+ return this.resolvedConfig().i18nNamespace;
46671
+ }
46672
+ constructor() {
46673
+ this.nav = inject(NavigationService);
46674
+ this.i18n = inject(I18nService);
46675
+ this.auth = inject(AuthService);
46676
+ this.orgService = inject(OrgService);
46677
+ this.modalService = inject(ModalService);
46678
+ this.toast = inject(ToastService);
46679
+ this.errors = inject(ValtechErrorService);
46680
+ this.route = inject(ActivatedRoute, { optional: true });
46681
+ this.resolvedConfig = computed(() => {
46682
+ const fromRoute = (this.route?.snapshot.data['accountConfig'] ?? {});
46683
+ const merged = { ...fromRoute, ...(this.config ?? {}) };
46684
+ return {
46685
+ showPendingInvites: merged.showPendingInvites ?? true,
46686
+ showOrganizations: merged.showOrganizations ?? true,
46687
+ showNewOrgCta: merged.showNewOrgCta ?? true,
46688
+ showLogout: merged.showLogout ?? true,
46689
+ showDeleteAccount: merged.showDeleteAccount ?? true,
46690
+ i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE,
46691
+ manageOrgRoute: merged.manageOrgRoute ?? DEFAULT_MANAGE_ORG_ROUTE,
46692
+ onOrgSwitched: merged.onOrgSwitched,
46693
+ onOrgCreated: merged.onOrgCreated,
46694
+ onLoggedOut: merged.onLoggedOut,
46695
+ onAccountDeleted: merged.onAccountDeleted,
46696
+ };
46697
+ });
46698
+ this.orgs = signal([]);
46699
+ this.orgsLoading = signal(false);
46700
+ this.orgsError = signal(null);
46701
+ this.orgsErrorState = computed(() => {
46702
+ this.i18n.lang();
46703
+ const err = this.orgsError();
46704
+ if (!err)
46705
+ return null;
46706
+ return createErrorStateProps(err, {
46707
+ title: {
46708
+ offline: this.tt('orgsOfflineTitle'),
46709
+ error: this.tt('orgsErrorTitle'),
46710
+ },
46711
+ description: {
46712
+ offline: this.tt('orgsOfflineHint'),
46713
+ error: interpretError(err).message,
46714
+ },
46715
+ retryLabel: this.tt('orgsRetry'),
46716
+ onRetry: () => this.loadOrgs(),
46717
+ retrying: this.orgsLoading(),
46718
+ });
46719
+ });
46720
+ this.pendingInvites = signal([]);
46721
+ this.pendingInvitesLoading = signal(false);
46722
+ this.inviteAccepting = signal(null);
46723
+ this.pageTitle = computed(() => this.tt('pageTitle'));
46724
+ this.pageDescription = computed(() => this.tt('pageDescription'));
46725
+ this.orgsTitle = computed(() => this.tt('orgsTitle'));
46726
+ this.orgsEmpty = computed(() => this.tt('orgsEmpty'));
46727
+ this.orgsMoreInfo = computed(() => this.tt('orgsMoreInfo'));
46728
+ this.orgsNewQuestion = computed(() => this.tt('orgsNewQuestion'));
46729
+ this.orgsNewHint = computed(() => this.tt('orgsNewHint'));
46730
+ this.newOrgCtaProps = computed(() => ({
46731
+ title: this.tt('orgsNewQuestion'),
46732
+ description: this.tt('orgsNewHint'),
46733
+ padding: '16px',
46734
+ borderRadius: '14px',
46735
+ actions: {
46736
+ position: 'right',
46737
+ columned: false,
46738
+ buttons: [
46739
+ {
46740
+ text: this.tt('orgsNewCta'),
46741
+ color: 'primary',
46742
+ fill: 'outline',
46743
+ shape: 'round',
46744
+ size: 'default',
46745
+ type: 'button',
46746
+ state: 'ENABLED',
46747
+ token: 'account-new-org',
46748
+ },
46749
+ ],
46750
+ },
46751
+ }));
46752
+ this.logoutTitle = computed(() => this.tt('logoutTitle'));
46753
+ this.logoutHint = computed(() => this.tt('logoutHint'));
46754
+ this.dangerTitle = computed(() => this.tt('dangerTitle'));
46755
+ this.dangerDeleteTitle = computed(() => this.tt('dangerDeleteTitle'));
46756
+ this.dangerDeleteHint = computed(() => this.tt('dangerDeleteHint'));
46757
+ this.activeOrgId = computed(() => {
46758
+ const u = this.auth.user();
46759
+ return u?.activeOrgId ?? u?.activeOrg ?? '';
46760
+ });
46761
+ this.activeOrgRole = computed(() => {
46762
+ const roles = this.auth.roles?.() ?? [];
46763
+ return roles[0] ?? null;
46764
+ });
46765
+ this.activeOrg = computed(() => this.orgs().find(o => o.id === this.activeOrgId()) ?? null);
46766
+ this.logoutButtonProps = computed(() => ({
46767
+ text: this.tt('logout'),
46768
+ color: 'dark',
46769
+ fill: 'outline',
46770
+ size: 'default',
46771
+ shape: 'round',
46772
+ type: 'button',
46773
+ token: 'account-logout',
46774
+ }));
46775
+ this.deleteAccountButtonProps = computed(() => ({
46776
+ text: this.tt('dangerDeleteCta'),
46777
+ color: 'dark',
46778
+ fill: 'outline',
46779
+ size: 'default',
46780
+ shape: 'round',
46781
+ type: 'button',
46782
+ token: 'account-delete',
46783
+ }));
46784
+ this.switchAccountButtonProps = computed(() => ({
46785
+ text: this.tt('switchAccountCta'),
46786
+ color: 'dark',
46787
+ fill: 'outline',
46788
+ size: 'default',
46789
+ shape: 'round',
46790
+ type: 'button',
46791
+ token: 'account-switch',
46792
+ }));
46793
+ const ns = this.ns;
46794
+ if (!this.i18n.hasNamespace(ns)) {
46795
+ this.i18n.registerContent(ns, ACCOUNT_VIEW_I18N);
46796
+ }
46797
+ this.nav.setBackHeader('pageTitle', ns, { withMenu: true });
46798
+ this.loadOrgs();
46799
+ this.loadPendingInvites();
46800
+ connectPageRefresh(async () => {
46801
+ await Promise.all([this.loadOrgs(), this.loadPendingInvites()]);
46802
+ });
46803
+ }
46804
+ orgTypeLabel(type) {
46805
+ const map = {
46806
+ PERSONAL: this.tt('orgsTypePersonal'),
46807
+ PRIVATE: this.tt('orgsTypePrivate'),
46808
+ PUBLIC: this.tt('orgsTypePublic'),
46809
+ BUSINESS: this.tt('orgsTypeBusiness'),
46810
+ };
46811
+ return map[type] ?? type;
46812
+ }
46813
+ onSwitchAccount() {
46814
+ void this.modalService.open({
46815
+ component: SwitchOrgModalComponent,
46816
+ componentProps: {
46817
+ i18nNamespace: 'Settings.SwitchOrg',
46818
+ onSuccess: () => {
46819
+ this.loadOrgs();
46820
+ this.loadPendingInvites();
46821
+ this.resolvedConfig().onOrgSwitched?.();
46822
+ },
46823
+ },
46824
+ breakpoints: {
46825
+ breakpoints: [0, 0.85],
46826
+ initialBreakpoint: 0.85,
46827
+ showHandle: true,
46828
+ },
46829
+ backdropDismiss: true,
46830
+ });
46831
+ }
46832
+ onDeleteAccount() {
46833
+ void this.modalService.open({
46834
+ component: DeleteAccountModalComponent,
46835
+ componentProps: {
46836
+ i18nNamespace: 'Settings.DeleteAccount',
46837
+ },
46838
+ breakpoints: {
46839
+ breakpoints: [0, 0.85],
46840
+ initialBreakpoint: 0.85,
46841
+ showHandle: true,
46842
+ },
46843
+ backdropDismiss: false,
46844
+ });
46845
+ this.resolvedConfig().onAccountDeleted?.();
46846
+ }
46847
+ onManageOrg() {
46848
+ this.nav.navigateByUrl(this.resolvedConfig().manageOrgRoute);
46849
+ }
46850
+ onNewOrg() {
46851
+ void this.modalService.open({
46852
+ component: CreateOrgModalComponent,
46853
+ componentProps: {
46854
+ i18nNamespace: 'CreateOrgModal',
46855
+ onSuccess: (newOrg) => {
46856
+ this.loadOrgs();
46857
+ this.resolvedConfig().onOrgCreated?.(newOrg);
46858
+ },
46859
+ },
46860
+ breakpoints: {
46861
+ breakpoints: [0, 0.92],
46862
+ initialBreakpoint: 0.92,
46863
+ showHandle: true,
46864
+ },
46865
+ backdropDismiss: true,
46866
+ });
46867
+ }
46868
+ onMoreInfo() {
46869
+ void this.modalService.open({
46870
+ component: OrgInfoSheetComponent,
46871
+ componentProps: {
46872
+ title: this.tt('orgsInfoTitle'),
46873
+ subtitle: this.tt('orgsInfoSubtitle'),
46874
+ body: this.tt('orgsInfoContent'),
46875
+ closeLabel: this.tt('orgsInfoClose'),
46876
+ closeProps: {
46877
+ text: this.tt('orgsInfoClose'),
46878
+ fill: 'outline',
46879
+ color: 'dark',
46880
+ size: 'default',
46881
+ expand: 'block',
46882
+ type: 'button',
46883
+ },
46884
+ },
46885
+ breakpoints: {
46886
+ breakpoints: [0, 0.85],
46887
+ initialBreakpoint: 0.85,
46888
+ showHandle: true,
46889
+ },
46890
+ backdropDismiss: true,
46891
+ });
46892
+ }
46893
+ onLogout() {
46894
+ this.auth.logoutWithConfirmation();
46895
+ this.resolvedConfig().onLoggedOut?.();
46896
+ }
46897
+ loadPendingInvites() {
46898
+ this.pendingInvitesLoading.set(true);
46899
+ return new Promise(resolve => {
46900
+ this.orgService
46901
+ .getPendingInvitations()
46902
+ .pipe(take(1))
46903
+ .subscribe({
46904
+ next: items => {
46905
+ this.pendingInvites.set(items);
46906
+ this.pendingInvitesLoading.set(false);
46907
+ resolve();
46908
+ },
46909
+ error: () => {
46910
+ this.pendingInvitesLoading.set(false);
46911
+ resolve();
46912
+ },
46913
+ });
46914
+ });
46915
+ }
46916
+ acceptPendingInvite(invite) {
46917
+ if (this.inviteAccepting())
46918
+ return;
46919
+ this.inviteAccepting.set(invite.orgId);
46920
+ this.orgService
46921
+ .acceptInvitation(invite.orgId)
46922
+ .pipe(take(1))
46923
+ .subscribe({
46924
+ next: () => {
46925
+ this.toast.show({
46926
+ message: this.tt('inviteAccepted'),
46927
+ color: 'dark',
46928
+ duration: 3500,
46929
+ });
46930
+ this.pendingInvites.update(list => list.filter(i => i.orgId !== invite.orgId));
46931
+ this.loadOrgs();
46932
+ },
46933
+ error: err => {
46934
+ this.errors.handle(err, { context: 'account.acceptPendingInvite' });
46935
+ this.inviteAccepting.set(null);
46936
+ },
46937
+ complete: () => this.inviteAccepting.set(null),
46938
+ });
46939
+ }
46940
+ declinePendingInvite(invite) {
46941
+ if (this.inviteAccepting())
46942
+ return;
46943
+ this.inviteAccepting.set(invite.orgId);
46944
+ this.orgService
46945
+ .declineInvitation(invite.orgId)
46946
+ .pipe(take(1))
46947
+ .subscribe({
46948
+ next: () => {
46949
+ this.toast.show({
46950
+ message: this.tt('inviteDeclined'),
46951
+ color: 'dark',
46952
+ duration: 2500,
46953
+ });
46954
+ this.pendingInvites.update(list => list.filter(i => i.orgId !== invite.orgId));
46955
+ },
46956
+ error: err => {
46957
+ this.errors.handle(err, { context: 'account.declinePendingInvite' });
46958
+ this.inviteAccepting.set(null);
46959
+ },
46960
+ complete: () => this.inviteAccepting.set(null),
46961
+ });
46962
+ }
46963
+ loadOrgs() {
46964
+ this.orgsLoading.set(true);
46965
+ this.orgsError.set(null);
46966
+ return new Promise(resolve => {
46967
+ this.orgService.listMyOrgs().subscribe({
46968
+ next: orgs => {
46969
+ const activeId = this.activeOrgId();
46970
+ this.orgs.set([...orgs].sort((a, b) => (a.id === activeId ? -1 : b.id === activeId ? 1 : 0)));
46971
+ this.orgsLoading.set(false);
46972
+ resolve();
46973
+ },
46974
+ error: err => {
46975
+ this.orgsError.set(err);
46976
+ this.orgsLoading.set(false);
46977
+ resolve();
46978
+ },
46979
+ });
46980
+ });
46981
+ }
46982
+ tt(key) {
46983
+ return this.i18n.t(key, this.ns);
46984
+ }
46985
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AccountViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
46986
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: AccountViewComponent, isStandalone: true, selector: "val-account-view", inputs: { config: "config" }, ngImport: i0, template: `
46987
+ <div class="page">
46988
+ <header class="page-header">
46989
+ <val-display [props]="{ size: 'small', color: 'dark', content: pageTitle() }" />
46990
+ <val-title
46991
+ [props]="{
46992
+ size: 'large',
46993
+ color: 'dark',
46994
+ bold: false,
46995
+ content: pageDescription(),
46996
+ }"
46997
+ />
46998
+ </header>
46999
+
47000
+ @if (resolvedConfig().showPendingInvites && pendingInvites().length > 0) {
47001
+ <val-title
47002
+ [props]="{
47003
+ size: 'medium',
47004
+ color: 'dark',
47005
+ bold: true,
47006
+ content: tt('pendingInvitesTitle'),
47007
+ }"
47008
+ />
47009
+ @for (invite of pendingInvites(); track invite.orgId) {
47010
+ <val-invitation-card
47011
+ [props]="{
47012
+ token: invite.orgId,
47013
+ orgName: invite.orgName,
47014
+ inviterName: invite.inviterName,
47015
+ role: invite.roleId,
47016
+ accepting: inviteAccepting() === invite.orgId,
47017
+ disabled: !!inviteAccepting(),
47018
+ acceptLabel: tt('accept'),
47019
+ declineLabel: tt('decline'),
47020
+ }"
47021
+ (onAccept)="acceptPendingInvite(invite)"
47022
+ (onDecline)="declinePendingInvite(invite)"
47023
+ />
47024
+ }
47025
+ }
47026
+
47027
+ @if (resolvedConfig().showOrganizations) {
47028
+ <!-- Organizations -->
47029
+ <section class="settings-section">
47030
+ <div class="section-header-row">
47031
+ <val-title
47032
+ [props]="{
47033
+ size: 'medium',
47034
+ color: 'dark',
47035
+ bold: true,
47036
+ content: orgsTitle(),
47037
+ }"
47038
+ />
47039
+ @if (orgs().length > 1) {
47040
+ <ion-button fill="outline" color="dark" shape="round" size="small" (click)="onSwitchAccount()">
47041
+ {{ tt('orgsViewAll') }}
47042
+ </ion-button>
47043
+ }
47044
+ </div>
47045
+ <div class="section-body">
47046
+ @if (orgsLoading()) {
47047
+ <val-skeleton-layout [props]="{ preset: 'list', rows: 3 }" />
47048
+ } @else if (orgsErrorState()) {
47049
+ <val-empty-state [props]="orgsErrorState()!" />
47050
+ } @else {
47051
+ @if (activeOrg(); as org) {
47052
+ <div class="orgs-list">
47053
+ <div class="org-card org-card--active">
47054
+ <div class="org-card__icon">
47055
+ <ion-icon name="business-outline" />
47056
+ </div>
47057
+ <div class="org-card__body">
47058
+ <span class="org-card__name">{{ org.name }}</span>
47059
+ <div class="org-card__meta">
47060
+ <span class="org-card__meta-item">
47061
+ <span class="org-card__meta-label">{{ tt('orgsLabelType') }}</span>
47062
+ {{ orgTypeLabel(org.type) }}
47063
+ </span>
47064
+ @if (activeOrgRole()) {
47065
+ <span class="org-card__meta-item">
47066
+ <span class="org-card__meta-label">{{ tt('orgsLabelRole') }}</span>
47067
+ {{ activeOrgRole() | titlecase }}
47068
+ </span>
47069
+ }
47070
+ <span class="org-card__meta-item">
47071
+ <span class="org-card__meta-label">{{ tt('orgsLabelStatus') }}</span>
47072
+ {{ tt('orgsStatusActive') }}
47073
+ </span>
47074
+ </div>
47075
+ </div>
47076
+ <div class="org-card__end">
47077
+ <ion-button fill="outline" color="dark" shape="round" size="small" (click)="onManageOrg()">
47078
+ {{ tt('orgsManage') }}
47079
+ </ion-button>
47080
+ </div>
47081
+ </div>
47082
+ </div>
47083
+ } @else if (orgs().length === 0) {
47084
+ <div class="orgs-empty-card">
47085
+ <div class="orgs-empty-card__main">
47086
+ <ion-icon name="people-outline" class="orgs-empty-card__icon" />
47087
+ <span class="orgs-empty-card__text">{{ orgsEmpty() }}</span>
47088
+ </div>
47089
+ <button class="orgs-empty-card__link" (click)="onMoreInfo()">
47090
+ {{ orgsMoreInfo() }}
47091
+ </button>
47092
+ </div>
47093
+ }
47094
+ @if (resolvedConfig().showNewOrgCta) {
47095
+ <!-- Nueva organizacion: CTA card con titulo, subtitulo y boton -->
47096
+ <val-cta-card class="org-new-cta-card" [props]="newOrgCtaProps()" (onAction)="onNewOrg()" />
47097
+ }
47098
+ }
47099
+ </div>
47100
+ </section>
47101
+ }
47102
+
47103
+ @if (resolvedConfig().showLogout) {
47104
+ <!-- Logout this session -->
47105
+ <section class="settings-section">
47106
+ <val-title
47107
+ [props]="{
47108
+ size: 'medium',
47109
+ color: 'dark',
47110
+ bold: true,
47111
+ content: logoutTitle(),
47112
+ }"
47113
+ />
47114
+ <div class="section-body">
47115
+ <val-text
47116
+ [props]="{
47117
+ content: logoutHint(),
47118
+ size: 'medium',
47119
+ color: 'dark',
47120
+ bold: false,
47121
+ }"
47122
+ />
47123
+ <div class="row-actions">
47124
+ <val-button [props]="logoutButtonProps()" (click)="onLogout()" />
47125
+ </div>
47126
+ </div>
47127
+ </section>
47128
+ }
47129
+
47130
+ @if (resolvedConfig().showDeleteAccount) {
47131
+ <!-- Delete account -->
47132
+ <section class="settings-section">
47133
+ <val-title
47134
+ [props]="{
47135
+ size: 'medium',
47136
+ color: 'dark',
47137
+ bold: true,
47138
+ content: dangerDeleteTitle(),
47139
+ }"
47140
+ />
47141
+ <div class="section-body">
47142
+ <val-text
47143
+ [props]="{
47144
+ content: dangerDeleteHint(),
47145
+ size: 'medium',
47146
+ color: 'dark',
47147
+ bold: false,
47148
+ }"
47149
+ />
47150
+ <div class="row-actions">
47151
+ <val-button [props]="deleteAccountButtonProps()" (onClick)="onDeleteAccount()" />
47152
+ </div>
47153
+ </div>
47154
+ </section>
47155
+ }
47156
+ </div>
47157
+ `, isInline: true, styles: [".page{padding:16px 0;max-width:720px;margin:0 auto}.page-header{margin-bottom:16px}.settings-section{padding:16px 0}.settings-section+.settings-section{border-top:1px solid var(--val-border-color, rgba(0, 0, 0, .08))}.settings-section val-title{display:block;margin-bottom:12px}.section-body{display:flex;flex-direction:column;gap:10px}.row-actions{margin-top:12px}.row-actions--gap{display:flex;flex-wrap:wrap;gap:8px}.section-header-row{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:8px}.org-new-cta-card{display:block;margin-top:12px}.orgs-empty-card{background:var(--ion-color-secondary-tint, rgba(var(--ion-color-secondary-rgb, 130, 101, 208), .2));border-radius:18px;padding:18px 16px 12px;display:flex;flex-direction:column;gap:8px}.orgs-empty-card__main{display:flex;align-items:center;gap:10px}.orgs-empty-card__icon{font-size:20px;flex-shrink:0;color:var(--ion-color-dark)}.orgs-empty-card__text{font-size:.875rem;color:var(--ion-color-dark)}.orgs-empty-card__link{background:none;border:none;padding:0;cursor:pointer;font-size:.8125rem;color:var(--ion-color-primary);text-align:left;text-decoration:underline;text-underline-offset:2px;font-family:inherit}.orgs-list{display:flex;flex-direction:column;gap:8px}.org-card{display:flex;align-items:center;gap:14px;padding:14px 16px;border-radius:12px;background:var(--ion-color-light, #f4f5f8);border:1.5px solid transparent;cursor:pointer;transition:background .15s,border-color .15s}.org-card:active{opacity:.75}.org-card--active{border-color:var(--ion-color-primary);background:color-mix(in srgb,var(--ion-color-primary) 8%,transparent)}:host-context(body.dark) .org-card,:host-context(html.ion-palette-dark) .org-card,:host-context([data-theme=\"dark\"]) .org-card{background:#ffffff0d}:host-context(body.dark) .org-card--active,:host-context(html.ion-palette-dark) .org-card--active,:host-context([data-theme=\"dark\"]) .org-card--active{background:color-mix(in srgb,var(--ion-color-primary) 15%,transparent)}.org-card__icon{width:40px;height:40px;border-radius:50%;background:color-mix(in srgb,var(--ion-color-primary) 15%,transparent);display:flex;align-items:center;justify-content:center;flex-shrink:0;font-size:20px;color:var(--ion-color-primary)}.org-card__body{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}.org-card__name{font-weight:600;font-size:.95rem;color:var(--ion-color-dark);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.org-card__meta{display:flex;flex-direction:column;gap:1px;margin-top:3px}.org-card__meta-item{font-size:.78rem;color:var(--ion-color-dark)}.org-card__meta-label{font-weight:600;color:var(--ion-color-dark);margin-right:4px}.org-card__end{display:flex;align-items:center;gap:8px;flex-shrink:0}\n"], dependencies: [{ kind: "component", type: DisplayComponent, selector: "val-display", inputs: ["props"] }, { kind: "component", type: EmptyStateComponent, selector: "val-empty-state", inputs: ["props"] }, { kind: "component", type: SkeletonLayoutComponent, selector: "val-skeleton-layout", inputs: ["props"] }, { kind: "component", type: TitleComponent, selector: "val-title", inputs: ["props"] }, { kind: "component", type: TextComponent, selector: "val-text", inputs: ["props"] }, { kind: "component", type: ButtonComponent, selector: "val-button", inputs: ["preset", "props"], outputs: ["onClick"] }, { kind: "component", type: CtaCardComponent, selector: "val-cta-card", inputs: ["props"], outputs: ["onAction"] }, { kind: "component", type: InvitationCardComponent, selector: "val-invitation-card", inputs: ["props"], outputs: ["onAccept", "onDecline"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { 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: "pipe", type: TitleCasePipe, name: "titlecase" }] }); }
47158
+ }
47159
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AccountViewComponent, decorators: [{
47160
+ type: Component,
47161
+ args: [{ selector: 'val-account-view', standalone: true, imports: [
47162
+ DisplayComponent,
47163
+ EmptyStateComponent,
47164
+ SkeletonLayoutComponent,
47165
+ TitleComponent,
47166
+ TextComponent,
47167
+ ButtonComponent,
47168
+ CtaCardComponent,
47169
+ InvitationCardComponent,
47170
+ IonIcon,
47171
+ IonButton,
47172
+ TitleCasePipe,
47173
+ ], template: `
47174
+ <div class="page">
47175
+ <header class="page-header">
47176
+ <val-display [props]="{ size: 'small', color: 'dark', content: pageTitle() }" />
47177
+ <val-title
47178
+ [props]="{
47179
+ size: 'large',
47180
+ color: 'dark',
47181
+ bold: false,
47182
+ content: pageDescription(),
47183
+ }"
47184
+ />
47185
+ </header>
47186
+
47187
+ @if (resolvedConfig().showPendingInvites && pendingInvites().length > 0) {
47188
+ <val-title
47189
+ [props]="{
47190
+ size: 'medium',
47191
+ color: 'dark',
47192
+ bold: true,
47193
+ content: tt('pendingInvitesTitle'),
47194
+ }"
47195
+ />
47196
+ @for (invite of pendingInvites(); track invite.orgId) {
47197
+ <val-invitation-card
47198
+ [props]="{
47199
+ token: invite.orgId,
47200
+ orgName: invite.orgName,
47201
+ inviterName: invite.inviterName,
47202
+ role: invite.roleId,
47203
+ accepting: inviteAccepting() === invite.orgId,
47204
+ disabled: !!inviteAccepting(),
47205
+ acceptLabel: tt('accept'),
47206
+ declineLabel: tt('decline'),
47207
+ }"
47208
+ (onAccept)="acceptPendingInvite(invite)"
47209
+ (onDecline)="declinePendingInvite(invite)"
47210
+ />
47211
+ }
47212
+ }
47213
+
47214
+ @if (resolvedConfig().showOrganizations) {
47215
+ <!-- Organizations -->
47216
+ <section class="settings-section">
47217
+ <div class="section-header-row">
47218
+ <val-title
47219
+ [props]="{
47220
+ size: 'medium',
47221
+ color: 'dark',
47222
+ bold: true,
47223
+ content: orgsTitle(),
47224
+ }"
47225
+ />
47226
+ @if (orgs().length > 1) {
47227
+ <ion-button fill="outline" color="dark" shape="round" size="small" (click)="onSwitchAccount()">
47228
+ {{ tt('orgsViewAll') }}
47229
+ </ion-button>
47230
+ }
47231
+ </div>
47232
+ <div class="section-body">
47233
+ @if (orgsLoading()) {
47234
+ <val-skeleton-layout [props]="{ preset: 'list', rows: 3 }" />
47235
+ } @else if (orgsErrorState()) {
47236
+ <val-empty-state [props]="orgsErrorState()!" />
47237
+ } @else {
47238
+ @if (activeOrg(); as org) {
47239
+ <div class="orgs-list">
47240
+ <div class="org-card org-card--active">
47241
+ <div class="org-card__icon">
47242
+ <ion-icon name="business-outline" />
47243
+ </div>
47244
+ <div class="org-card__body">
47245
+ <span class="org-card__name">{{ org.name }}</span>
47246
+ <div class="org-card__meta">
47247
+ <span class="org-card__meta-item">
47248
+ <span class="org-card__meta-label">{{ tt('orgsLabelType') }}</span>
47249
+ {{ orgTypeLabel(org.type) }}
47250
+ </span>
47251
+ @if (activeOrgRole()) {
47252
+ <span class="org-card__meta-item">
47253
+ <span class="org-card__meta-label">{{ tt('orgsLabelRole') }}</span>
47254
+ {{ activeOrgRole() | titlecase }}
47255
+ </span>
47256
+ }
47257
+ <span class="org-card__meta-item">
47258
+ <span class="org-card__meta-label">{{ tt('orgsLabelStatus') }}</span>
47259
+ {{ tt('orgsStatusActive') }}
47260
+ </span>
47261
+ </div>
47262
+ </div>
47263
+ <div class="org-card__end">
47264
+ <ion-button fill="outline" color="dark" shape="round" size="small" (click)="onManageOrg()">
47265
+ {{ tt('orgsManage') }}
47266
+ </ion-button>
47267
+ </div>
47268
+ </div>
47269
+ </div>
47270
+ } @else if (orgs().length === 0) {
47271
+ <div class="orgs-empty-card">
47272
+ <div class="orgs-empty-card__main">
47273
+ <ion-icon name="people-outline" class="orgs-empty-card__icon" />
47274
+ <span class="orgs-empty-card__text">{{ orgsEmpty() }}</span>
47275
+ </div>
47276
+ <button class="orgs-empty-card__link" (click)="onMoreInfo()">
47277
+ {{ orgsMoreInfo() }}
47278
+ </button>
47279
+ </div>
47280
+ }
47281
+ @if (resolvedConfig().showNewOrgCta) {
47282
+ <!-- Nueva organizacion: CTA card con titulo, subtitulo y boton -->
47283
+ <val-cta-card class="org-new-cta-card" [props]="newOrgCtaProps()" (onAction)="onNewOrg()" />
47284
+ }
47285
+ }
47286
+ </div>
47287
+ </section>
47288
+ }
47289
+
47290
+ @if (resolvedConfig().showLogout) {
47291
+ <!-- Logout this session -->
47292
+ <section class="settings-section">
47293
+ <val-title
47294
+ [props]="{
47295
+ size: 'medium',
47296
+ color: 'dark',
47297
+ bold: true,
47298
+ content: logoutTitle(),
47299
+ }"
47300
+ />
47301
+ <div class="section-body">
47302
+ <val-text
47303
+ [props]="{
47304
+ content: logoutHint(),
47305
+ size: 'medium',
47306
+ color: 'dark',
47307
+ bold: false,
47308
+ }"
47309
+ />
47310
+ <div class="row-actions">
47311
+ <val-button [props]="logoutButtonProps()" (click)="onLogout()" />
47312
+ </div>
47313
+ </div>
47314
+ </section>
47315
+ }
47316
+
47317
+ @if (resolvedConfig().showDeleteAccount) {
47318
+ <!-- Delete account -->
47319
+ <section class="settings-section">
47320
+ <val-title
47321
+ [props]="{
47322
+ size: 'medium',
47323
+ color: 'dark',
47324
+ bold: true,
47325
+ content: dangerDeleteTitle(),
47326
+ }"
47327
+ />
47328
+ <div class="section-body">
47329
+ <val-text
47330
+ [props]="{
47331
+ content: dangerDeleteHint(),
47332
+ size: 'medium',
47333
+ color: 'dark',
47334
+ bold: false,
47335
+ }"
47336
+ />
47337
+ <div class="row-actions">
47338
+ <val-button [props]="deleteAccountButtonProps()" (onClick)="onDeleteAccount()" />
47339
+ </div>
47340
+ </div>
47341
+ </section>
47342
+ }
47343
+ </div>
47344
+ `, styles: [".page{padding:16px 0;max-width:720px;margin:0 auto}.page-header{margin-bottom:16px}.settings-section{padding:16px 0}.settings-section+.settings-section{border-top:1px solid var(--val-border-color, rgba(0, 0, 0, .08))}.settings-section val-title{display:block;margin-bottom:12px}.section-body{display:flex;flex-direction:column;gap:10px}.row-actions{margin-top:12px}.row-actions--gap{display:flex;flex-wrap:wrap;gap:8px}.section-header-row{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:8px}.org-new-cta-card{display:block;margin-top:12px}.orgs-empty-card{background:var(--ion-color-secondary-tint, rgba(var(--ion-color-secondary-rgb, 130, 101, 208), .2));border-radius:18px;padding:18px 16px 12px;display:flex;flex-direction:column;gap:8px}.orgs-empty-card__main{display:flex;align-items:center;gap:10px}.orgs-empty-card__icon{font-size:20px;flex-shrink:0;color:var(--ion-color-dark)}.orgs-empty-card__text{font-size:.875rem;color:var(--ion-color-dark)}.orgs-empty-card__link{background:none;border:none;padding:0;cursor:pointer;font-size:.8125rem;color:var(--ion-color-primary);text-align:left;text-decoration:underline;text-underline-offset:2px;font-family:inherit}.orgs-list{display:flex;flex-direction:column;gap:8px}.org-card{display:flex;align-items:center;gap:14px;padding:14px 16px;border-radius:12px;background:var(--ion-color-light, #f4f5f8);border:1.5px solid transparent;cursor:pointer;transition:background .15s,border-color .15s}.org-card:active{opacity:.75}.org-card--active{border-color:var(--ion-color-primary);background:color-mix(in srgb,var(--ion-color-primary) 8%,transparent)}:host-context(body.dark) .org-card,:host-context(html.ion-palette-dark) .org-card,:host-context([data-theme=\"dark\"]) .org-card{background:#ffffff0d}:host-context(body.dark) .org-card--active,:host-context(html.ion-palette-dark) .org-card--active,:host-context([data-theme=\"dark\"]) .org-card--active{background:color-mix(in srgb,var(--ion-color-primary) 15%,transparent)}.org-card__icon{width:40px;height:40px;border-radius:50%;background:color-mix(in srgb,var(--ion-color-primary) 15%,transparent);display:flex;align-items:center;justify-content:center;flex-shrink:0;font-size:20px;color:var(--ion-color-primary)}.org-card__body{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}.org-card__name{font-weight:600;font-size:.95rem;color:var(--ion-color-dark);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.org-card__meta{display:flex;flex-direction:column;gap:1px;margin-top:3px}.org-card__meta-item{font-size:.78rem;color:var(--ion-color-dark)}.org-card__meta-label{font-weight:600;color:var(--ion-color-dark);margin-right:4px}.org-card__end{display:flex;align-items:center;gap:8px;flex-shrink:0}\n"] }]
47345
+ }], ctorParameters: () => [], propDecorators: { config: [{
47346
+ type: Input
47347
+ }] } });
47348
+
47349
+ /**
47350
+ * Helper para montar la vista Cuenta (`val-account-view`) como ruta en una app
47351
+ * del factory. El `config` se pasa por route `data` (`accountConfig`) y el
47352
+ * componente lo lee como fallback de su `@Input() config`.
47353
+ *
47354
+ * @example
47355
+ * ```ts
47356
+ * // settings.routes.ts
47357
+ * export const settingsRoutes: Routes = [
47358
+ * ...provideValtechAccountRoutes(),
47359
+ * { path: 'preferences', loadComponent: () => ... },
47360
+ * ];
47361
+ *
47362
+ * // con config acotada:
47363
+ * ...provideValtechAccountRoutes({ config: { showDeleteAccount: false } }),
47364
+ * ...provideValtechAccountRoutes({ config: { manageOrgRoute: '/app/org' } }),
47365
+ * ```
47366
+ */
47367
+ function provideValtechAccountRoutes(opts) {
47368
+ return [
47369
+ {
47370
+ path: opts?.path ?? 'account',
47371
+ component: AccountViewComponent,
47372
+ data: { accountConfig: opts?.config },
47373
+ },
47374
+ ];
47375
+ }
47376
+
47377
+ /** Built-in label sets for the three platform locales. */
47378
+ const CALLOUT_LABELS = {
47379
+ es: {
47380
+ NOTE: 'Nota',
47381
+ TIP: 'Tip',
47382
+ INFO: 'Info',
47383
+ IMPORTANT: 'Importante',
47384
+ WARNING: 'Atención',
47385
+ CAUTION: 'Precaución',
47386
+ },
47387
+ en: {
47388
+ NOTE: 'Note',
47389
+ TIP: 'Tip',
47390
+ INFO: 'Info',
47391
+ IMPORTANT: 'Important',
47392
+ WARNING: 'Warning',
47393
+ CAUTION: 'Caution',
47394
+ },
47395
+ pt: {
47396
+ NOTE: 'Nota',
47397
+ TIP: 'Dica',
47398
+ INFO: 'Info',
47399
+ IMPORTANT: 'Importante',
47400
+ WARNING: 'Atenção',
47401
+ CAUTION: 'Cuidado',
47402
+ },
47403
+ };
47404
+ /**
47405
+ * Per-callout-kind color (Ionic semantic color). Fixed across locales since
47406
+ * red = danger no matter what language you speak.
47407
+ */
47408
+ const CALLOUT_COLORS = {
47409
+ NOTE: 'primary',
47410
+ TIP: 'success',
47411
+ INFO: 'tertiary',
47412
+ IMPORTANT: 'warning',
47413
+ WARNING: 'warning',
47414
+ CAUTION: 'danger',
47415
+ };
47416
+ const BOX_DRAWING = /[┌┐└┘├┤┬┴┼─│╔╗╚╝═║]/;
47417
+ const CALLOUT_RE = /^>\s*\[!(NOTE|TIP|INFO|WARNING|CAUTION|IMPORTANT)\]\s*(.*)$/i;
47418
+ const FENCE_RE = /^```([\w-]*)\s*$/;
47419
+ const HEADING_RE = /^(#{1,6})\s+(.+)$/;
47420
+ const SEPARATOR_RE = /^\s*(?:-{3,}|\*{3,}|_{3,})\s*$/;
47421
+ const UNORDERED_RE = /^\s*[-*+]\s+(.+)$/;
47422
+ const CHECKLIST_RE = /^\s*[-*+]\s+\[([ xX])\]\s+(.+)$/;
47423
+ const ORDERED_RE = /^\s*\d+\.\s+(.+)$/;
47424
+ const TABLE_DIVIDER_RE = /^\s*\|?[\s:|-]+\|?\s*$/;
47425
+ const TABLE_ROW_RE = /^\s*\|(.+)\|\s*$/;
47426
+ /**
47427
+ * Pure Markdown → ArticleMetadata parser. No Angular deps — usable from Node scripts
47428
+ * (build-time generation) and from the Angular `MarkdownArticleParserService` wrapper.
47429
+ *
47430
+ * Supported syntax:
47431
+ * - Headings (#, ##, ### …) → title / subtitle
47432
+ * - Paragraphs → paragraph (with `processLinks` + `allowPartialBold`)
47433
+ * - Lists (-, *, 1.) → unordered / ordered list (- [ ] / - [x] for checklist)
47434
+ * - Blockquotes (>) → quote, with GitHub callouts `> [!NOTE|TIP|INFO|WARNING|CAUTION|IMPORTANT]` mapped to notes
47435
+ * - Code fences (```lang) → code
47436
+ * - Box-drawing ASCII art (┌┐└┘│─) auto-wrapped as code
47437
+ * - Tables → flattened into paragraphs with bold keys
47438
+ * - Horizontal rules (---, ***, ___) → separator
47439
+ */
47440
+ function parseMarkdownArticle(markdown, options) {
47441
+ const lines = normalize(markdown).split('\n');
47442
+ const elements = [];
47443
+ const labels = options?.calloutLabels ??
47444
+ (options?.locale ? CALLOUT_LABELS[options.locale] : CALLOUT_LABELS.es);
47445
+ let i = 0;
47446
+ while (i < lines.length) {
47447
+ const line = lines[i];
47448
+ if (line.trim() === '') {
47449
+ i++;
47450
+ continue;
47451
+ }
47452
+ const fence = line.match(FENCE_RE);
47453
+ if (fence) {
47454
+ const lang = fence[1] || undefined;
47455
+ const body = [];
47456
+ i++;
47457
+ while (i < lines.length && !FENCE_RE.test(lines[i])) {
47458
+ body.push(lines[i]);
47459
+ i++;
47460
+ }
47461
+ i++;
47462
+ elements.push({
47463
+ type: 'code',
47464
+ props: { code: body.join('\n'), language: lang, theme: 'dark' },
47465
+ });
47466
+ continue;
47467
+ }
47468
+ if (BOX_DRAWING.test(line)) {
47469
+ const body = [];
47470
+ while (i < lines.length && (lines[i].trim() === '' || BOX_DRAWING.test(lines[i]))) {
47471
+ body.push(lines[i]);
47472
+ i++;
47473
+ }
47474
+ while (body.length && body[body.length - 1].trim() === '')
47475
+ body.pop();
47476
+ elements.push({
47477
+ type: 'code',
47478
+ props: { code: body.join('\n'), language: 'text', theme: 'dark' },
47479
+ });
47480
+ continue;
47481
+ }
47482
+ if (SEPARATOR_RE.test(line)) {
47483
+ elements.push({ type: 'separator', props: { style: 'line' } });
47484
+ i++;
47485
+ continue;
47486
+ }
47487
+ const heading = line.match(HEADING_RE);
47488
+ if (heading) {
47489
+ elements.push(makeHeading(heading[1].length, heading[2].trim()));
47490
+ i++;
47491
+ continue;
47492
+ }
47493
+ if (line.startsWith('>')) {
47494
+ const block = [];
47495
+ while (i < lines.length && lines[i].startsWith('>')) {
47496
+ block.push(lines[i]);
47497
+ i++;
47498
+ }
47499
+ elements.push(makeQuoteOrCallout(block, labels));
47500
+ continue;
47501
+ }
47502
+ if (TABLE_ROW_RE.test(line)) {
47503
+ const rows = [];
47504
+ while (i < lines.length && TABLE_ROW_RE.test(lines[i])) {
47505
+ rows.push(lines[i]);
47506
+ i++;
47507
+ }
47508
+ elements.push(...makeTable(rows));
47509
+ continue;
47510
+ }
47511
+ if (CHECKLIST_RE.test(line) || UNORDERED_RE.test(line) || ORDERED_RE.test(line)) {
47512
+ const listType = CHECKLIST_RE.test(line)
47513
+ ? 'checklist'
47514
+ : ORDERED_RE.test(line)
47515
+ ? 'ordered'
47516
+ : 'unordered';
47517
+ const items = [];
47518
+ while (i < lines.length && lines[i].trim() !== '') {
47519
+ const cur = lines[i];
47520
+ const check = cur.match(CHECKLIST_RE);
47521
+ const unord = cur.match(UNORDERED_RE);
47522
+ const ord = cur.match(ORDERED_RE);
47523
+ if (check)
47524
+ items.push({ text: processInlineBold(check[2].trim()) });
47525
+ else if (ord && listType === 'ordered')
47526
+ items.push({ text: processInlineBold(ord[1].trim()) });
47527
+ else if (unord && listType !== 'ordered')
47528
+ items.push({ text: processInlineBold(unord[1].trim()) });
47529
+ else
47530
+ break;
47531
+ i++;
47532
+ }
47533
+ elements.push({ type: 'list', props: { items, listType } });
47534
+ continue;
47535
+ }
47536
+ const paragraph = [];
47537
+ while (i < lines.length && lines[i].trim() !== '' && !startsNewBlock(lines[i])) {
47538
+ paragraph.push(lines[i]);
47539
+ i++;
47540
+ }
47541
+ const text = processInlineBold(paragraph.join(' ').trim());
47542
+ if (text) {
47543
+ elements.push({
47544
+ type: 'paragraph',
47545
+ props: {
47546
+ content: text,
47547
+ size: 'medium',
47548
+ color: 'dark',
47549
+ bold: false,
47550
+ processLinks: true,
47551
+ allowPartialBold: true,
47552
+ },
47553
+ });
47554
+ }
47555
+ }
47556
+ // Strip parser-only options before merging into ArticleMetadata
47557
+ const { locale: _l, calloutLabels: _c, ...metadataOverrides } = options ?? {};
45462
47558
  return {
45463
47559
  elements,
45464
47560
  maxWidth: '900px',
@@ -47314,102 +49410,6 @@ function beautifyLegalArticle(article) {
47314
49410
  };
47315
49411
  }
47316
49412
 
47317
- class OrgService {
47318
- constructor(config, http) {
47319
- this.config = config;
47320
- this.http = http;
47321
- }
47322
- get baseUrl() {
47323
- return `${this.config.apiUrl}/org`;
47324
- }
47325
- listMyOrgs() {
47326
- return this.http.get(`${this.baseUrl}/my`).pipe(map$1(r => r.organizations));
47327
- }
47328
- listMyOrgsPage(params) {
47329
- const queryParams = new URLSearchParams();
47330
- if (params?.limit)
47331
- queryParams.set('limit', String(params.limit));
47332
- if (params?.nextToken)
47333
- queryParams.set('nextToken', params.nextToken);
47334
- const qs = queryParams.toString();
47335
- const url = `${this.baseUrl}/my${qs ? '?' + qs : ''}`;
47336
- return this.http.get(url);
47337
- }
47338
- getOrg(orgId) {
47339
- return this.http.get(`${this.baseUrl}/${orgId}`).pipe(map$1(r => r.organization));
47340
- }
47341
- createOrg(req) {
47342
- return this.http.post(`${this.baseUrl}`, req).pipe(map$1(r => r.organization));
47343
- }
47344
- updateOrg(orgId, req) {
47345
- return this.http
47346
- .put(`${this.baseUrl}/${orgId}`, req)
47347
- .pipe(map$1(r => r.organization));
47348
- }
47349
- deleteOrg(orgId) {
47350
- return this.http.delete(`${this.baseUrl}/${orgId}`);
47351
- }
47352
- inviteUser(orgId, req) {
47353
- return this.http.post(`${this.baseUrl}/${orgId}/invite`, req);
47354
- }
47355
- leaveOrg(orgId) {
47356
- return this.http.post(`${this.baseUrl}/${orgId}/leave`, {});
47357
- }
47358
- getOrgMembers(orgId, params) {
47359
- const queryParams = new URLSearchParams();
47360
- if (params?.limit)
47361
- queryParams.set('limit', String(params.limit));
47362
- if (params?.nextToken)
47363
- queryParams.set('nextToken', params.nextToken);
47364
- const qs = queryParams.toString();
47365
- const url = `${this.baseUrl}/${orgId}/members${qs ? '?' + qs : ''}`;
47366
- return this.http.get(url);
47367
- }
47368
- listOrgRoles(orgId) {
47369
- return this.http
47370
- .get(`${this.baseUrl}/${orgId}/roles`)
47371
- .pipe(map$1(r => r.roles ?? []));
47372
- }
47373
- changeMemberRole(orgId, userId, roleId) {
47374
- const body = { roleId };
47375
- return this.http.put(`${this.baseUrl}/${orgId}/members/${userId}/role`, body);
47376
- }
47377
- removeMember(orgId, userId) {
47378
- return this.http.delete(`${this.baseUrl}/${orgId}/members/${userId}`);
47379
- }
47380
- transferOwnership(orgId, newOwnerId) {
47381
- return this.http
47382
- .post(`${this.baseUrl}/${orgId}/transfer-ownership`, { newOwnerId })
47383
- .pipe(map$1(r => r.organization));
47384
- }
47385
- validateInviteToken(token) {
47386
- return this.http.post(`${this.config.apiUrl}/v2/actions/validate`, { token });
47387
- }
47388
- executeInviteToken(token) {
47389
- return this.http.post(`${this.config.apiUrl}/v2/actions/execute`, { token });
47390
- }
47391
- getPendingInvitations() {
47392
- return this.http
47393
- .get(`${this.config.apiUrl}/organizations/invitations/pending`)
47394
- .pipe(map$1(r => r.items ?? []));
47395
- }
47396
- acceptInvitation(orgId) {
47397
- return this.http.post(`${this.baseUrl}/invitations/${orgId}/accept`, {});
47398
- }
47399
- declineInvitation(orgId) {
47400
- return this.http.post(`${this.baseUrl}/invitations/${orgId}/decline`, {});
47401
- }
47402
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: OrgService, deps: [{ token: VALTECH_AUTH_CONFIG }, { token: i1$3.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable }); }
47403
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: OrgService, providedIn: 'root' }); }
47404
- }
47405
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: OrgService, decorators: [{
47406
- type: Injectable,
47407
- args: [{ providedIn: 'root' }]
47408
- }], ctorParameters: () => [{ type: undefined, decorators: [{
47409
- type: Inject,
47410
- args: [VALTECH_AUTH_CONFIG]
47411
- }] }, { type: i1$3.HttpClient }] });
47412
-
47413
49413
  /**
47414
49414
  * Preferences types — Fase 1 schema simple (theme + language + notifications.master).
47415
49415
  * Doc canónico: /apps/{appId}/users/{uid}/preferences/main
@@ -55698,5 +57698,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
55698
57698
  * Generated bundle index. Do not edit.
55699
57699
  */
55700
57700
 
55701
- 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, 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, 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, DetailSkeletonComponent, DeviceService, DisplayComponent, DividerComponent, DocsApiTableComponent, DocsBreadcrumbComponent, DocsBuilder, DocsCalloutComponent, DocsCodeExampleComponent, DocsLayoutComponent, DocsNavLinksComponent, DocsNavigationService, DocsPageComponent, DocsSearchComponent, DocsSectionComponent, DocsShellComponent, DocsSidebarComponent, DocsTocComponent, DonationService, DownloadService, 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, 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, MenuComponent, MessagingService, MetaService, MetricCardComponent, MfaModalComponent, ModalService, ModalShellComponent, MultiSelectSearchComponent, NUM_KEYS, NavigationService, NetworkBannerComponent, NetworkStatusService, NewsBuilder, NoContentComponent, NotesBoxComponent, NotificationActionService, NotificationsService, NumberFromToComponent, NumberInputComponent, NumberStepperComponent, OAUTH_PROVIDERS_INFO, OAuthCallbackComponent, OAuthService, OrgService, OrgSwitchService, OutlineDefault, OutlineDefaultBlock, OutlineDefaultFull, OutlineDefaultRound, OutlineDefaultRoundBlock, OutlineDefaultRoundFull, PATTERN_MOTIFS, PATTERN_PALETTES, PATTERN_STYLE_CONFIGS, PLATFORM_CONFIGS, PageContentComponent, PageLinksComponent, PageRefreshService, PageTemplateComponent, PageWavesComponent, PageWrapperComponent, PaginationComponent, PaginationService, PasswordInputComponent, PatternComponent, PhoneInputComponent, PillComponent, PinInputComponent, PlainCodeBoxComponent, PopoverSelectorComponent, PreferencesService, PreferencesViewComponent, PresetService, PriceTagComponent, PrimarySolidBlockButton, PrimarySolidBlockHrefButton, PrimarySolidBlockIconButton, PrimarySolidBlockIconHrefButton, PrimarySolidDefaultRoundButton, PrimarySolidDefaultRoundHrefButton, PrimarySolidDefaultRoundIconButton, PrimarySolidDefaultRoundIconHrefButton, PrimarySolidFullButton, PrimarySolidFullHrefButton, PrimarySolidFullIconButton, PrimarySolidFullIconHrefButton, PrimarySolidLargeRoundButton, PrimarySolidLargeRoundHrefButton, PrimarySolidLargeRoundIconButton, PrimarySolidLargeRoundIconHrefButton, PrimarySolidSmallRoundButton, PrimarySolidSmallRoundHrefButton, PrimarySolidSmallRoundIconButton, PrimarySolidSmallRoundIconHrefButton, ProcessLinksPipe, ProfileSkeletonComponent, ProfileViewComponent, ProgressBarComponent, ProgressRingComponent, ProgressStatusComponent, PrompterComponent, QR_PRESETS, QrCodeComponent, QrGeneratorService, QueryBuilder, QuoteBoxComponent, REQUEST_STATUSES, RadioInputComponent, RangeInputComponent, RatingComponent, RefresherComponent, RequestFirestoreService, RequestFormBuilderService, RequestService, RightsFooterComponent, RotatingTextComponent, SEARCH_HEADER_DEFAULTS, SETTINGS_SECTIONS_CATALOG, SHAPE_KEYS, SHARE_PROFILE_MODAL_DEFAULTS, SKELETON_LAYOUT_DEFAULT_ROWS, SKELETON_PRESETS, SOLID_KEYS, STATS_BAR_DEFAULTS, STROKE_KEYS, SearchHeaderComponent, SearchSelectorComponent, SearchbarComponent, SecondarySolidBlockButton, SecondarySolidBlockHrefButton, SecondarySolidBlockIconButton, SecondarySolidBlockIconHrefButton, SecondarySolidDefaultRoundButton, SecondarySolidDefaultRoundHrefButton, SecondarySolidDefaultRoundIconButton, SecondarySolidDefaultRoundIconHrefButton, SecondarySolidFullButton, SecondarySolidFullHrefButton, SecondarySolidFullIconButton, SecondarySolidFullIconHrefButton, SecondarySolidLargeRoundButton, SecondarySolidLargeRoundHrefButton, SecondarySolidLargeRoundIconButton, SecondarySolidLargeRoundIconHrefButton, SecondarySolidSmallRoundButton, SecondarySolidSmallRoundHrefButton, SecondarySolidSmallRoundIconButton, SecondarySolidSmallRoundIconHrefButton, SecurityViewComponent, SegmentControlComponent, SelectSearchComponent, SessionListModalComponent, SessionService, SettingsHubComponent, ShareButtonsComponent, ShareProfileModalComponent, SimpleComponent, SkeletonComponent, SkeletonLayoutComponent, SkeletonService, SolidBlockButton, SolidDefault, SolidDefaultBlock, SolidDefaultButton, SolidDefaultFull, SolidDefaultRound, SolidDefaultRoundBlock, SolidDefaultRoundButton, SolidDefaultRoundFull, SolidFullButton, SolidLargeButton, SolidLargeRoundButton, SolidSmallButton, SolidSmallRoundButton, SplashScreenService, StatsBarComponent, StatsCardComponent, StepperComponent, StorageService, SwipeCarouselComponent, TRI_KEYS, TabbedContentComponent, TableSkeletonComponent, TabsComponent, Terminal404Component, TestimonialCardComponent, TestimonialCarouselComponent, TextComponent, TextInputComponent, TextareaInputComponent, ThemeOption, ThemeService, TimelineComponent, TitleBlockComponent, TitleComponent, ToastService, ToggleInputComponent, TokenService, ToolbarActionType, ToolbarComponent, 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, provideValtechAds, provideValtechAppConfig, provideValtechAppVersion, provideValtechAuth, provideValtechAuthInterceptor, provideValtechContent, provideValtechDebugConsole, provideValtechDonations, provideValtechErrorHandling, provideValtechFeedback, provideValtechFirebase, provideValtechI18n, provideValtechLegal, provideValtechPreferencesRoutes, provideValtechPresets, provideValtechProfileRoutes, provideValtechSecurityRoutes, provideValtechSettingsRoutes, provideValtechSite, provideValtechSkeleton, query, renderPatternSvgInner, replaceSpecialChars, resolveColor$1 as resolveColor, resolveInputDefaultValue, resolveWebBaseUrl, roleGuard, storagePaths, superAdminGuard, toArticle };
57701
+ 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, 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, 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, MenuComponent, MessagingService, MetaService, MetricCardComponent, MfaModalComponent, ModalService, ModalShellComponent, MultiSelectSearchComponent, NUM_KEYS, NavigationService, NetworkBannerComponent, NetworkStatusService, NewsBuilder, NoContentComponent, NotesBoxComponent, NotificationActionService, NotificationsService, NumberFromToComponent, NumberInputComponent, NumberStepperComponent, OAUTH_PROVIDERS_INFO, OAuthCallbackComponent, OAuthService, OrgInfoSheetComponent, OrgService, OrgSwitchService, OutlineDefault, OutlineDefaultBlock, OutlineDefaultFull, OutlineDefaultRound, OutlineDefaultRoundBlock, OutlineDefaultRoundFull, PATTERN_MOTIFS, PATTERN_PALETTES, PATTERN_STYLE_CONFIGS, PLATFORM_CONFIGS, PageContentComponent, PageLinksComponent, PageRefreshService, PageTemplateComponent, PageWavesComponent, PageWrapperComponent, PaginationComponent, PaginationService, PasswordInputComponent, PatternComponent, PhoneInputComponent, PillComponent, PinInputComponent, PlainCodeBoxComponent, PopoverSelectorComponent, PreferencesService, PreferencesViewComponent, PresetService, PriceTagComponent, PrimarySolidBlockButton, PrimarySolidBlockHrefButton, PrimarySolidBlockIconButton, PrimarySolidBlockIconHrefButton, PrimarySolidDefaultRoundButton, PrimarySolidDefaultRoundHrefButton, PrimarySolidDefaultRoundIconButton, PrimarySolidDefaultRoundIconHrefButton, PrimarySolidFullButton, PrimarySolidFullHrefButton, PrimarySolidFullIconButton, PrimarySolidFullIconHrefButton, PrimarySolidLargeRoundButton, PrimarySolidLargeRoundHrefButton, PrimarySolidLargeRoundIconButton, PrimarySolidLargeRoundIconHrefButton, PrimarySolidSmallRoundButton, PrimarySolidSmallRoundHrefButton, PrimarySolidSmallRoundIconButton, PrimarySolidSmallRoundIconHrefButton, ProcessLinksPipe, ProfileSkeletonComponent, ProfileViewComponent, ProgressBarComponent, ProgressRingComponent, ProgressStatusComponent, PrompterComponent, QR_PRESETS, QrCodeComponent, QrGeneratorService, QueryBuilder, QuoteBoxComponent, REQUEST_STATUSES, RadioInputComponent, RangeInputComponent, RatingComponent, RefresherComponent, RequestFirestoreService, RequestFormBuilderService, RequestService, RightsFooterComponent, RotatingTextComponent, SEARCH_HEADER_DEFAULTS, SETTINGS_SECTIONS_CATALOG, SHAPE_KEYS, SHARE_PROFILE_MODAL_DEFAULTS, SKELETON_LAYOUT_DEFAULT_ROWS, SKELETON_PRESETS, SOLID_KEYS, STATS_BAR_DEFAULTS, STROKE_KEYS, SearchHeaderComponent, SearchSelectorComponent, SearchbarComponent, SecondarySolidBlockButton, SecondarySolidBlockHrefButton, SecondarySolidBlockIconButton, SecondarySolidBlockIconHrefButton, SecondarySolidDefaultRoundButton, SecondarySolidDefaultRoundHrefButton, SecondarySolidDefaultRoundIconButton, SecondarySolidDefaultRoundIconHrefButton, SecondarySolidFullButton, SecondarySolidFullHrefButton, SecondarySolidFullIconButton, SecondarySolidFullIconHrefButton, SecondarySolidLargeRoundButton, SecondarySolidLargeRoundHrefButton, SecondarySolidLargeRoundIconButton, SecondarySolidLargeRoundIconHrefButton, SecondarySolidSmallRoundButton, SecondarySolidSmallRoundHrefButton, SecondarySolidSmallRoundIconButton, SecondarySolidSmallRoundIconHrefButton, SecurityViewComponent, SegmentControlComponent, SelectSearchComponent, SessionListModalComponent, SessionService, SettingsHubComponent, ShareButtonsComponent, ShareProfileModalComponent, SimpleComponent, SkeletonComponent, SkeletonLayoutComponent, SkeletonService, SolidBlockButton, SolidDefault, SolidDefaultBlock, SolidDefaultButton, SolidDefaultFull, SolidDefaultRound, SolidDefaultRoundBlock, SolidDefaultRoundButton, SolidDefaultRoundFull, SolidFullButton, SolidLargeButton, SolidLargeRoundButton, SolidSmallButton, SolidSmallRoundButton, SplashScreenService, StatsBarComponent, StatsCardComponent, StepperComponent, StorageService, SwipeCarouselComponent, SwitchOrgModalComponent, TRI_KEYS, TabbedContentComponent, TableSkeletonComponent, TabsComponent, Terminal404Component, TestimonialCardComponent, TestimonialCarouselComponent, TextComponent, TextInputComponent, TextareaInputComponent, ThemeOption, ThemeService, TimelineComponent, TitleBlockComponent, TitleComponent, ToastService, ToggleInputComponent, TokenService, ToolbarActionType, ToolbarComponent, TranslatePipe, TypedCollection, UPDATE_BANNER_DEFAULT_CONTENT, UPDATE_BANNER_I18N_NAMESPACE, UpdateBannerComponent, UserAvatarComponent, UsernameInputComponent, VALTECH_ADS_CONFIG, VALTECH_APP_CONFIG, VALTECH_APP_VERSION, VALTECH_AUTH_CONFIG, VALTECH_COMPANY_LINKS, VALTECH_CONTENT_CONFIG, VALTECH_DEBUG_CONSOLE, VALTECH_DEFAULT_CONTENT, VALTECH_DONATION_CONFIG, VALTECH_FEEDBACK_CONFIG, VALTECH_FIREBASE_CONFIG, VALTECH_FOOTER_I18N, VALTECH_FOOTER_LOGO, VALTECH_LANGUAGE_SELECTOR, VALTECH_LEGAL_CONFIG, VALTECH_NETWORK_ERROR_KEY, VALTECH_SITE_PATHS, VALTECH_SOCIAL_LINKS, VALTECH_SPLASH_SCREEN, VALTECH_WEB_BASE_URLS, VERSION, ValtechErrorService, WizardComponent, WizardFooterComponent, applyDefaultValueToControl, authGuard, authInterceptor, authPasswordValidator, beautifyLegalArticle, blogPost, buildCompanyFooterProps, buildFooterLinks, buildLegalLinkResolver, buildPath, buildSettingsCards, collections, connectPageRefresh, createErrorStateProps, createFirebaseConfig, createGlowCardProps, createInitialPaginationState, createNumberFromToField, createRefreshableStream, createTitleProps, docs, errorLoggingInterceptor, extractPathParams, generatePatternTiles, generateRandomTile, getAppInfo, getAppVersion, getCollectionPath, getDocumentId, getTimeOfDayKey, goToTop, guestGuard, hasEmulators, interpretError, isAtEnd, isCollectionPath, isDocumentPath, isEmulatorMode, isValidPath, joinPath, maxLength, mulberry32, news, parseMarkdownArticle, permissionGuard, permissionGuardFromRoute, provideLegalContent, provideSplashScreen, provideValtechAccountRoutes, provideValtechAds, provideValtechAppConfig, provideValtechAppVersion, provideValtechAuth, provideValtechAuthInterceptor, provideValtechContent, provideValtechDebugConsole, provideValtechDonations, provideValtechErrorHandling, provideValtechFeedback, provideValtechFirebase, provideValtechI18n, provideValtechLegal, provideValtechPreferencesRoutes, provideValtechPresets, provideValtechProfileRoutes, provideValtechSecurityRoutes, provideValtechSettingsRoutes, provideValtechSite, provideValtechSkeleton, query, renderPatternSvgInner, replaceSpecialChars, resolveColor$1 as resolveColor, resolveInputDefaultValue, resolveWebBaseUrl, roleGuard, storagePaths, superAdminGuard, toArticle };
55702
57702
  //# sourceMappingURL=valtech-components.mjs.map