valtech-components 4.0.25 → 4.0.27
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.
- package/README.md +31 -262
- package/esm2022/lib/components/molecules/content-reaction/content-reaction.component.mjs +4 -7
- package/esm2022/lib/components/molecules/modal-shell/modal-shell.component.mjs +3 -3
- package/esm2022/lib/components/organisms/api-key-create-modal/api-key-create-modal.component.mjs +7 -7
- package/esm2022/lib/components/organisms/api-keys-modal/api-keys-modal.component.mjs +13 -13
- package/esm2022/lib/components/organisms/avatar-upload/avatar-upload.component.mjs +3 -3
- package/esm2022/lib/components/organisms/change-password-modal/change-password-modal.component.mjs +5 -5
- package/esm2022/lib/components/organisms/create-org-modal/create-org-modal.component.mjs +5 -5
- package/esm2022/lib/components/organisms/delete-account-modal/delete-account-modal.component.mjs +4 -3
- package/esm2022/lib/components/organisms/edit-org-modal/edit-org-modal.component.mjs +5 -5
- package/esm2022/lib/components/organisms/invite-member-modal/invite-member-modal.component.mjs +7 -5
- package/esm2022/lib/components/organisms/login-attempt-modal/login-attempt-modal.component.mjs +6 -3
- package/esm2022/lib/components/organisms/member-detail-modal/member-detail-modal.component.mjs +6 -5
- package/esm2022/lib/components/organisms/member-import-modal/member-import-modal.component.mjs +9 -9
- package/esm2022/lib/components/organisms/mfa-modal/mfa-modal.component.mjs +3 -3
- package/esm2022/lib/components/organisms/org-info-sheet/org-info-sheet.component.mjs +37 -48
- package/esm2022/lib/components/organisms/organization-view/organization-view.component.mjs +8 -2
- package/esm2022/lib/components/organisms/organization-view/organization-view.i18n.mjs +15 -3
- package/esm2022/lib/components/organisms/session-list-modal/session-list-modal.component.mjs +4 -3
- package/esm2022/lib/components/organisms/switch-org-modal/switch-org-modal.component.mjs +3 -3
- package/esm2022/lib/components/organisms/transfer-ownership-modal/transfer-ownership-modal.component.mjs +7 -7
- package/esm2022/lib/services/firebase/messaging.service.mjs +2 -2
- package/esm2022/lib/services/refreshable-stream/refreshable-stream.mjs +2 -2
- package/esm2022/lib/version.mjs +2 -2
- package/fesm2022/valtech-components.mjs +136 -122
- package/fesm2022/valtech-components.mjs.map +1 -1
- package/lib/components/organisms/org-info-sheet/org-info-sheet.component.d.ts +12 -1
- package/lib/version.d.ts +1 -1
- package/package.json +1 -1
- package/src/lib/services/firebase/firebase-messaging-sw.js +0 -145
|
@@ -25,7 +25,7 @@ import { getApps, getApp, initializeApp as initializeApp$1 } from 'firebase/app'
|
|
|
25
25
|
import * as i2$1 from '@ionic/angular/standalone';
|
|
26
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, IonRadio, IonRadioGroup, IonTextarea, IonDatetime, IonSelect, IonSelectOption, IonPopover, IonList, IonItem, IonInput, 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, cloudUploadOutline, 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, notificationsOffOutline, alarmOutline, chatboxEllipsesOutline, checkmarkDoneOutline, cogOutline, shareSocialOutline, helpCircleOutline, menuOutline, stopCircleOutline, videocamOffOutline } 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, cloudUploadOutline, 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, codeSlashOutline, notificationsOffOutline, alarmOutline, chatboxEllipsesOutline, checkmarkDoneOutline, cogOutline, shareSocialOutline, helpCircleOutline, menuOutline, stopCircleOutline, videocamOffOutline } 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';
|
|
@@ -56,7 +56,7 @@ import { BrowserMultiFormatReader } from '@zxing/browser';
|
|
|
56
56
|
* Current version of valtech-components.
|
|
57
57
|
* This is automatically updated during the publish process.
|
|
58
58
|
*/
|
|
59
|
-
const VERSION = '4.0.
|
|
59
|
+
const VERSION = '4.0.27';
|
|
60
60
|
|
|
61
61
|
// Control de estado de refresco (singleton a nivel de módulo)
|
|
62
62
|
let isRefreshing = false;
|
|
@@ -5499,7 +5499,7 @@ class MessagingService {
|
|
|
5499
5499
|
const token = await this.requestPermission();
|
|
5500
5500
|
if (!token) {
|
|
5501
5501
|
// requestPermission devuelve null tanto por permiso denegado como por
|
|
5502
|
-
// fallo al obtener el token.
|
|
5502
|
+
// fallo al obtener el token. Distivdsmos por el estado del permiso.
|
|
5503
5503
|
const permission = this.getPermissionState();
|
|
5504
5504
|
if (permission === 'denied' || permission === 'default') {
|
|
5505
5505
|
return { status: 'denied', reason: 'Permiso de notificaciones denegado' };
|
|
@@ -29795,7 +29795,7 @@ class ModalShellComponent {
|
|
|
29795
29795
|
<ion-toolbar>
|
|
29796
29796
|
@if (showClose()) {
|
|
29797
29797
|
<ion-buttons slot="end">
|
|
29798
|
-
<ion-button fill="clear" color="dark" (click)="onClose()">
|
|
29798
|
+
<ion-button fill="clear" color="dark" shape="round" (click)="onClose()">
|
|
29799
29799
|
<strong>{{ resolvedCloseLabel() }}</strong>
|
|
29800
29800
|
</ion-button>
|
|
29801
29801
|
</ion-buttons>
|
|
@@ -29820,7 +29820,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
29820
29820
|
<ion-toolbar>
|
|
29821
29821
|
@if (showClose()) {
|
|
29822
29822
|
<ion-buttons slot="end">
|
|
29823
|
-
<ion-button fill="clear" color="dark" (click)="onClose()">
|
|
29823
|
+
<ion-button fill="clear" color="dark" shape="round" (click)="onClose()">
|
|
29824
29824
|
<strong>{{ resolvedCloseLabel() }}</strong>
|
|
29825
29825
|
</ion-button>
|
|
29826
29826
|
</ion-buttons>
|
|
@@ -33391,7 +33391,7 @@ class ChangePasswordModalComponent {
|
|
|
33391
33391
|
},
|
|
33392
33392
|
],
|
|
33393
33393
|
actions: {
|
|
33394
|
-
...button({ text: '', type: 'submit', fill: 'solid', expand: 'block' }),
|
|
33394
|
+
...button({ text: '', type: 'submit', fill: 'solid', expand: 'block', shape: 'round' }),
|
|
33395
33395
|
token: 'set-submit',
|
|
33396
33396
|
textKey: 'setPasswordSubmit',
|
|
33397
33397
|
},
|
|
@@ -33425,7 +33425,7 @@ class ChangePasswordModalComponent {
|
|
|
33425
33425
|
},
|
|
33426
33426
|
],
|
|
33427
33427
|
actions: {
|
|
33428
|
-
...button({ text: '', type: 'submit', fill: 'solid', expand: 'block' }),
|
|
33428
|
+
...button({ text: '', type: 'submit', fill: 'solid', expand: 'block', shape: 'round' }),
|
|
33429
33429
|
token: 'change-submit',
|
|
33430
33430
|
textKey: 'changePasswordSubmit',
|
|
33431
33431
|
},
|
|
@@ -33564,11 +33564,11 @@ class ChangePasswordModalComponent {
|
|
|
33564
33564
|
this.toast.show({ message, duration: 3500 });
|
|
33565
33565
|
}
|
|
33566
33566
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ChangePasswordModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
33567
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: ChangePasswordModalComponent, isStandalone: true, selector: "val-change-password-modal", inputs: { isOpen: "isOpen" }, outputs: { changed: "changed", dismissed: "dismissed" }, ngImport: i0, template: "<ion-modal [isOpen]=\"isOpen\" (didDismiss)=\"close()\">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-buttons slot=\"end\">\n <ion-button fill=\"clear\" color=\"dark\" (click)=\"close()\">\n <strong>{{ t('close') }}</strong>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n </ion-header>\n <ion-content class=\"ion-padding\">\n <section class=\"modal-form-section\">\n @if (mode() === 'loading') {\n <div class=\"modal-loading\">\n <ion-spinner name=\"crescent\"></ion-spinner>\n </div>\n } @else {\n <val-form [props]=\"formProps()\" (onSubmit)=\"submitHandler($event)\" />\n }\n </section>\n </ion-content>\n </ng-template>\n</ion-modal>\n", styles: [".modal-form-section{display:flex;flex-direction:column;gap:16px;max-width:420px;margin:0 auto;padding-top:8px}.modal-loading{display:flex;justify-content:center;padding:40px 0}\n"], dependencies: [{ 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: IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: IonModal, selector: "ion-modal" }, { kind: "component", type: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { kind: "component", type: IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "component", type: FormComponent, selector: "val-form", inputs: ["props"], outputs: ["onSubmit", "onInvalid", "onSelectChange"] }] }); }
|
|
33567
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: ChangePasswordModalComponent, isStandalone: true, selector: "val-change-password-modal", inputs: { isOpen: "isOpen" }, outputs: { changed: "changed", dismissed: "dismissed" }, ngImport: i0, template: "<ion-modal [isOpen]=\"isOpen\" (didDismiss)=\"close()\">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-buttons slot=\"end\">\n <ion-button fill=\"clear\" color=\"dark\" shape=\"round\" (click)=\"close()\">\n <strong>{{ t('close') }}</strong>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n </ion-header>\n <ion-content class=\"ion-padding\">\n <section class=\"modal-form-section\">\n @if (mode() === 'loading') {\n <div class=\"modal-loading\">\n <ion-spinner name=\"crescent\"></ion-spinner>\n </div>\n } @else {\n <val-form [props]=\"formProps()\" (onSubmit)=\"submitHandler($event)\" />\n }\n </section>\n </ion-content>\n </ng-template>\n</ion-modal>\n", styles: [".modal-form-section{display:flex;flex-direction:column;gap:16px;max-width:420px;margin:0 auto;padding-top:8px}.modal-loading{display:flex;justify-content:center;padding:40px 0}\n"], dependencies: [{ 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: IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: IonModal, selector: "ion-modal" }, { kind: "component", type: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { kind: "component", type: IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "component", type: FormComponent, selector: "val-form", inputs: ["props"], outputs: ["onSubmit", "onInvalid", "onSelectChange"] }] }); }
|
|
33568
33568
|
}
|
|
33569
33569
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ChangePasswordModalComponent, decorators: [{
|
|
33570
33570
|
type: Component,
|
|
33571
|
-
args: [{ selector: 'val-change-password-modal', standalone: true, imports: [IonButton, IonButtons, IonContent, IonHeader, IonModal, IonSpinner, IonToolbar, FormComponent], template: "<ion-modal [isOpen]=\"isOpen\" (didDismiss)=\"close()\">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-buttons slot=\"end\">\n <ion-button fill=\"clear\" color=\"dark\" (click)=\"close()\">\n <strong>{{ t('close') }}</strong>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n </ion-header>\n <ion-content class=\"ion-padding\">\n <section class=\"modal-form-section\">\n @if (mode() === 'loading') {\n <div class=\"modal-loading\">\n <ion-spinner name=\"crescent\"></ion-spinner>\n </div>\n } @else {\n <val-form [props]=\"formProps()\" (onSubmit)=\"submitHandler($event)\" />\n }\n </section>\n </ion-content>\n </ng-template>\n</ion-modal>\n", styles: [".modal-form-section{display:flex;flex-direction:column;gap:16px;max-width:420px;margin:0 auto;padding-top:8px}.modal-loading{display:flex;justify-content:center;padding:40px 0}\n"] }]
|
|
33571
|
+
args: [{ selector: 'val-change-password-modal', standalone: true, imports: [IonButton, IonButtons, IonContent, IonHeader, IonModal, IonSpinner, IonToolbar, FormComponent], template: "<ion-modal [isOpen]=\"isOpen\" (didDismiss)=\"close()\">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-buttons slot=\"end\">\n <ion-button fill=\"clear\" color=\"dark\" shape=\"round\" (click)=\"close()\">\n <strong>{{ t('close') }}</strong>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n </ion-header>\n <ion-content class=\"ion-padding\">\n <section class=\"modal-form-section\">\n @if (mode() === 'loading') {\n <div class=\"modal-loading\">\n <ion-spinner name=\"crescent\"></ion-spinner>\n </div>\n } @else {\n <val-form [props]=\"formProps()\" (onSubmit)=\"submitHandler($event)\" />\n }\n </section>\n </ion-content>\n </ng-template>\n</ion-modal>\n", styles: [".modal-form-section{display:flex;flex-direction:column;gap:16px;max-width:420px;margin:0 auto;padding-top:8px}.modal-loading{display:flex;justify-content:center;padding:40px 0}\n"] }]
|
|
33572
33572
|
}], propDecorators: { isOpen: [{
|
|
33573
33573
|
type: Input
|
|
33574
33574
|
}], changed: [{
|
|
@@ -35789,7 +35789,7 @@ class MfaModalComponent {
|
|
|
35789
35789
|
this.toast.show({ message, duration: 3500 });
|
|
35790
35790
|
}
|
|
35791
35791
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MfaModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
35792
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: MfaModalComponent, isStandalone: true, selector: "val-mfa-modal", inputs: { isOpen: "isOpen", prefillCode: "prefillCode" }, outputs: { changed: "changed", enabledViaDeeplink: "enabledViaDeeplink", dismissed: "dismissed" }, ngImport: i0, template: "<ion-modal [isOpen]=\"isOpen\" (didDismiss)=\"close()\" cssClass=\"val-modal-adaptive\">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-buttons slot=\"end\">\n <ion-button fill=\"clear\" color=\"dark\" (click)=\"close()\">\n <strong>{{ t('close') }}</strong>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n </ion-header>\n\n <ion-content class=\"ion-padding\">\n <section class=\"mfa-modal\">\n <val-display [props]=\"{ content: t('mfaManageTitle'), size: 'small', color: 'dark' }\" />\n\n @switch (step()) { @case ('loading') {\n <div class=\"mfa-loading\">\n <ion-spinner name=\"crescent\"></ion-spinner>\n </div>\n } @case ('status') { @if (mfaEnabled()) {\n <p class=\"mfa-status mfa-status--on\">{{ t('mfaEnabledLabel') }} \u00B7 {{ methodLabel(mfaMethod()) }}</p>\n\n @if (mfaMethod() === 'TOTP') {\n <div class=\"mfa-block\">\n <val-title [props]=\"{ size: 'small', color: 'dark', bold: true, content: t('mfaBackupCodesTitle') }\" />\n <val-text\n [props]=\"{ size: 'medium', color: 'dark', bold: false, content: t('mfaBackupCodesAvailable') + ': ' + backupCodesCount() }\"\n />\n\n @if (regeneratedCodes(); as codes) {\n <div class=\"mfa-alert\">\n <ion-icon name=\"information-circle-outline\"></ion-icon>\n <p>{{ t('mfaBackupCodesSaveWarning') }} {{ t('mfaBackupCodesExplain') }}</p>\n </div>\n <div class=\"mfa-codes\">\n @for (code of codes; track code) {\n <code class=\"mfa-code\">{{ code }}</code>\n }\n </div>\n <ion-button expand=\"block\" color=\"dark\" fill=\"outline\" (click)=\"copyCodes(codes)\">\n {{ t('mfaCopyCodes') }}\n </ion-button>\n } @else { @if (backupCodesCount() < 3) {\n <div class=\"mfa-alert\">\n <ion-icon name=\"information-circle-outline\"></ion-icon>\n <p>{{ t('mfaBackupCodesLow') }}</p>\n </div>\n }\n <ion-button\n expand=\"block\"\n color=\"dark\"\n fill=\"outline\"\n [disabled]=\"working()\"\n (click)=\"regenerateBackupCodes()\"\n >\n @if (working()) {\n <ion-spinner name=\"crescent\"></ion-spinner>\n } @else { {{ t('mfaRegenerateCodes') }} }\n </ion-button>\n }\n </div>\n }\n\n <ion-button expand=\"block\" color=\"dark\" fill=\"outline\" (click)=\"goToDisable()\">\n {{ t('mfaDisableButton') }}\n </ion-button>\n } @else {\n <p class=\"mfa-status mfa-status--off\">{{ t('mfaDisabledLabel') }}</p>\n <val-text [props]=\"{ size: 'medium', color: 'dark', bold: false, content: t('mfaDisabledHint') }\" />\n <ion-button expand=\"block\" (click)=\"goToMethodSelect()\"> {{ t('mfaEnableButton') }} </ion-button>\n } } @case ('method-select') {\n <val-title [props]=\"{ content: t('mfaEnableTitle'), size: 'large', color: 'dark', bold: false }\" />\n <val-text [props]=\"{ size: 'medium', color: 'dark', bold: false, content: t('mfaMethodPrompt') }\" />\n\n <div class=\"mfa-method-list\" role=\"radiogroup\">\n <button\n type=\"button\"\n class=\"mfa-method-card\"\n [class.mfa-method-card--active]=\"selectedMethod() === 'TOTP'\"\n (click)=\"selectedMethod.set('TOTP')\"\n role=\"radio\"\n [attr.aria-checked]=\"selectedMethod() === 'TOTP'\"\n >\n <span class=\"mfa-method-card__dot\"></span>\n <span class=\"mfa-method-card__body\">\n <strong>{{ t('mfaMethodTotp') }}</strong>\n <span>{{ t('mfaMethodTotpHint') }}</span>\n </span>\n </button>\n <button\n type=\"button\"\n class=\"mfa-method-card\"\n [class.mfa-method-card--active]=\"selectedMethod() === 'EMAIL'\"\n (click)=\"selectedMethod.set('EMAIL')\"\n role=\"radio\"\n [attr.aria-checked]=\"selectedMethod() === 'EMAIL'\"\n >\n <span class=\"mfa-method-card__dot\"></span>\n <span class=\"mfa-method-card__body\">\n <strong>{{ t('mfaMethodEmail') }}</strong>\n <span>{{ t('mfaMethodEmailHint') }}</span>\n </span>\n </button>\n <button\n type=\"button\"\n class=\"mfa-method-card\"\n [class.mfa-method-card--active]=\"selectedMethod() === 'SMS'\"\n (click)=\"selectedMethod.set('SMS')\"\n role=\"radio\"\n [attr.aria-checked]=\"selectedMethod() === 'SMS'\"\n >\n <span class=\"mfa-method-card__dot\"></span>\n <span class=\"mfa-method-card__body\">\n <strong>{{ t('mfaMethodSms') }}</strong>\n <span>{{ t('mfaMethodSmsHint') }}</span>\n </span>\n </button>\n </div>\n\n @if (selectedMethod() === 'SMS' && !userPhone()) {\n <ion-input\n label=\"{{ t('mfaPhoneLabel') }}\"\n labelPlacement=\"floating\"\n fill=\"outline\"\n type=\"tel\"\n placeholder=\"+56912345678\"\n [formControl]=\"phoneControl\"\n ></ion-input>\n @if (phoneControl.invalid && phoneControl.touched) {\n <p class=\"mfa-error\">{{ t('mfaPhoneInvalid') }}</p>\n } } @if (selectedMethod() === 'SMS' && userPhone(); as phone) {\n <val-text\n [props]=\"{ size: 'medium', color: 'dark', bold: false, content: t('mfaPhoneRegistered') + ': ' + phone }\"\n />\n }\n\n <ion-button expand=\"block\" [disabled]=\"working()\" (click)=\"proceedWithMethod()\">\n @if (working()) {\n <ion-spinner name=\"crescent\"></ion-spinner>\n } @else { {{ t('mfaContinue') }} }\n </ion-button>\n <ion-button expand=\"block\" fill=\"clear\" color=\"dark\" (click)=\"backToStatus()\">\n {{ t('mfaCancel') }}\n </ion-button>\n } @case ('totp-setup') {\n <val-title [props]=\"{ content: t('mfaTotpSetupTitle'), size: 'large', color: '', bold: false }\" />\n <val-text [props]=\"{ size: 'medium', color: 'dark', bold: false, content: t('mfaTotpStep1') }\" />\n\n @if (totpQr(); as qr) {\n <div class=\"mfa-qr\">\n <val-qr-code [props]=\"{ qr: qr }\" />\n </div>\n } @if (totpSetup(); as setup) {\n <val-text [props]=\"{ size: 'medium', color: 'dark', bold: false, content: t('mfaTotpManualEntry') }\" />\n <div class=\"mfa-secret-row\">\n <code class=\"mfa-secret\">{{ setup.secret }}</code>\n <ion-button fill=\"clear\" size=\"small\" (click)=\"copySecret(setup.secret)\" [attr.aria-label]=\"t('copy')\">\n @if (copiedSecret()) {\n <ion-icon name=\"checkmark-outline\"></ion-icon>\n } @else {\n <ion-icon name=\"copy-outline\"></ion-icon>\n }\n </ion-button>\n </div>\n\n <val-text [props]=\"{ size: 'medium', color: 'dark', bold: false, content: t('mfaTotpStep2') }\" />\n <div class=\"mfa-pin\">\n <val-pin-input [props]=\"pinInputProps\" />\n </div>\n\n <ion-button expand=\"block\" [disabled]=\"working()\" (click)=\"verifyTotp()\">\n @if (working()) {\n <ion-spinner name=\"crescent\"></ion-spinner>\n } @else { {{ t('mfaTotpVerify') }} }\n </ion-button>\n\n <div class=\"mfa-backup\">\n <val-title [props]=\"{ size: 'small', color: 'dark', bold: true, content: t('mfaBackupCodesTitle') }\" />\n <div class=\"mfa-alert\">\n <ion-icon name=\"information-circle-outline\"></ion-icon>\n <p>{{ t('mfaBackupCodesSaveWarning') }} {{ t('mfaBackupCodesExplain') }}</p>\n </div>\n <div class=\"mfa-codes\">\n @for (code of setup.backupCodes; track code) {\n <code class=\"mfa-code\">{{ code }}</code>\n }\n </div>\n <ion-button expand=\"block\" color=\"dark\" fill=\"outline\" (click)=\"copyCodes(setup.backupCodes)\">\n {{ t('mfaCopyCodes') }}\n </ion-button>\n </div>\n }\n\n <ion-button expand=\"block\" fill=\"clear\" color=\"dark\" (click)=\"backToStatus()\">\n {{ t('mfaCancel') }}\n </ion-button>\n } @case ('code-confirm') {\n <val-title [props]=\"{ content: t('mfaConfirmTitle'), size: 'large', color: '', bold: false }\" />\n <val-text\n [props]=\"{ size: 'medium', color: 'dark', bold: false, content: selectedMethod() === 'EMAIL' ? t('mfaConfirmPromptEmail') : t('mfaConfirmPromptSms') }\"\n />\n\n <div class=\"mfa-pin\">\n <val-pin-input [props]=\"pinInputProps\" />\n </div>\n\n <ion-button expand=\"block\" [disabled]=\"working()\" (click)=\"confirmCode()\">\n @if (working()) {\n <ion-spinner name=\"crescent\"></ion-spinner>\n } @else { {{ t('mfaConfirmButton') }} }\n </ion-button>\n\n <p class=\"mfa-resend\">\n {{ t('mfaNoCode') }} @if (resendCooldown() > 0) {\n <span>{{ t('mfaResendIn') }} {{ resendCooldown() }}s</span>\n } @else {\n <a (click)=\"resendCode()\">{{ t('mfaResend') }}</a>\n }\n </p>\n\n <ion-button expand=\"block\" fill=\"clear\" color=\"dark\" (click)=\"backToStatus()\">\n {{ t('mfaCancel') }}\n </ion-button>\n } @case ('disable') { @if (hasPassword()) {\n <val-form [props]=\"disableFormProps()\" (onSubmit)=\"onDisableSubmit($event)\" />\n } @else if (mfaMethod() === 'TOTP') {\n <val-title [props]=\"{ content: t('mfaDisableTitle'), size: 'large', color: '', bold: false }\" />\n <val-text [props]=\"{ size: 'medium', color: 'dark', bold: false, content: t('mfaDisableTotpPrompt') }\" />\n <div class=\"mfa-pin\">\n <val-pin-input [props]=\"pinInputProps\" />\n </div>\n <ion-button expand=\"block\" [disabled]=\"working()\" (click)=\"disableWithMfaCode()\">\n @if (working()) {\n <ion-spinner name=\"crescent\"></ion-spinner>\n } @else { {{ t('mfaDisableButton') }} }\n </ion-button>\n } @else if (mfaMethod() === 'EMAIL' || mfaMethod() === 'SMS') {\n <val-title [props]=\"{ content: t('mfaDisableTitle'), size: 'large', color: '', bold: false }\" />\n @if (!disableCodeSent()) {\n <val-text [props]=\"{ size: 'medium', color: 'dark', bold: false, content: t('mfaDisableCodePrompt') }\" />\n <ion-button expand=\"block\" [disabled]=\"working()\" (click)=\"sendDisableCode()\">\n @if (working()) {\n <ion-spinner name=\"crescent\"></ion-spinner>\n } @else { {{ t('mfaDisableSendCode') }} }\n </ion-button>\n } @else {\n <val-text [props]=\"{ size: 'medium', color: 'dark', bold: false, content: t('mfaDisableCodePrompt') }\" />\n <div class=\"mfa-pin\">\n <val-pin-input [props]=\"pinInputProps\" />\n </div>\n <ion-button expand=\"block\" [disabled]=\"working()\" (click)=\"disableWithMfaCode()\">\n @if (working()) {\n <ion-spinner name=\"crescent\"></ion-spinner>\n } @else { {{ t('mfaDisableButton') }} }\n </ion-button>\n <ion-button expand=\"block\" fill=\"clear\" color=\"medium\" [disabled]=\"working()\" (click)=\"sendDisableCode()\">\n {{ t('mfaDisableResendCode') }}\n </ion-button>\n } } @else {\n <val-title [props]=\"{ content: t('mfaDisableTitle'), size: 'large', color: '', bold: false }\" />\n <val-text [props]=\"{ size: 'medium', color: 'dark', bold: false, content: t('mfaDisableNeedsPassword') }\" />\n }\n <ion-button expand=\"block\" fill=\"clear\" color=\"dark\" (click)=\"backToStatus()\">\n {{ t('mfaCancel') }}\n </ion-button>\n } }\n </section>\n </ion-content>\n </ng-template>\n</ion-modal>\n", styles: [".mfa-modal{display:flex;flex-direction:column;gap:14px;padding-top:4px}.mfa-loading{display:flex;justify-content:center;padding:40px 0}.mfa-status{display:inline-flex;align-items:center;width:fit-content;padding:4px 10px;border-radius:999px;font-size:.8125rem;font-weight:600;margin:0}.mfa-status--on{color:var(--ion-color-success-shade);background:rgba(var(--ion-color-success-rgb),.12)}.mfa-status--off{color:var(--ion-color-medium-shade);background:var(--ion-color-light)}.mfa-secret-row{display:flex;align-items:center;gap:8px}.mfa-secret-row .mfa-secret{flex:1;margin:0}.mfa-secret-row ion-button{flex-shrink:0;--color: var(--ion-color-dark);margin:0}.mfa-error{color:var(--ion-color-danger);font-size:.8125rem;margin:4px 0 0}.mfa-block,.mfa-backup{display:flex;flex-direction:column;gap:12px}.mfa-block{padding:16px;border-radius:14px;background:var(--ion-color-light)}.mfa-alert{display:flex;align-items:flex-start;gap:10px;padding:12px 14px;border-radius:10px;background:var(--ion-color-light);border-left:3px solid var(--ion-color-primary)}.mfa-alert ion-icon{font-size:1.25rem;color:var(--ion-color-primary);flex-shrink:0;margin-top:1px}.mfa-alert p{margin:0;font-size:.8125rem;line-height:1.45;color:var(--ion-color-dark)}.mfa-qr{display:flex;justify-content:center;padding:8px 0}.mfa-secret{display:block;padding:10px 12px;border-radius:8px;background:var(--ion-color-light);font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.9375rem;letter-spacing:.04em;word-break:break-all;text-align:center}.mfa-pin{display:flex;justify-content:center;padding:8px 0}.mfa-codes{display:grid;grid-template-columns:repeat(2,1fr);gap:8px;padding:12px;border-radius:12px;background:var(--ion-color-light)}.mfa-code{padding:8px;border-radius:6px;background:var(--ion-background-color, #fff);color:var(--ion-color-dark);font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.8125rem;text-align:center}.mfa-method-list{display:flex;flex-direction:column;gap:8px}.mfa-method-card{display:flex;align-items:flex-start;gap:12px;padding:14px 16px;border-radius:12px;border:1.5px solid var(--ion-border-color, rgba(var(--ion-color-dark-rgb), .14));background:transparent;cursor:pointer;text-align:left;width:100%;transition:border-color .15s ease,background .15s ease}.mfa-method-card--active{border-color:var(--ion-color-primary);background:rgba(var(--ion-color-primary-rgb),.07)}.mfa-method-card__dot{flex-shrink:0;width:18px;height:18px;border-radius:50%;border:2px solid var(--ion-color-medium);margin-top:2px;transition:border-color .15s,box-shadow .15s}.mfa-method-card--active .mfa-method-card__dot{border-color:var(--ion-color-primary);box-shadow:inset 0 0 0 4px var(--ion-color-primary)}.mfa-method-card__body{display:flex;flex-direction:column;gap:3px}.mfa-method-card__body strong{font-size:.9375rem;font-weight:600;color:var(--ion-color-dark)}.mfa-method-card__body span{font-size:.8125rem;color:var(--ion-color-medium);line-height:1.4}.mfa-resend{text-align:center;font-size:.875rem;color:var(--ion-color-dark);margin:4px 0}.mfa-resend a{color:var(--ion-color-primary);cursor:pointer;font-weight:600}\n"], dependencies: [{ 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: IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: IonInput, selector: "ion-input", inputs: ["accept", "autocapitalize", "autocomplete", "autocorrect", "autofocus", "clearInput", "clearOnEdit", "color", "counter", "counterFormatter", "debounce", "disabled", "enterkeyhint", "errorText", "fill", "helperText", "inputmode", "label", "labelPlacement", "max", "maxlength", "min", "minlength", "mode", "multiple", "name", "pattern", "placeholder", "readonly", "required", "shape", "size", "spellcheck", "step", "type", "value"] }, { kind: "component", type: IonModal, selector: "ion-modal" }, { kind: "component", type: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { kind: "component", type: IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "component", type: DisplayComponent, selector: "val-display", inputs: ["props"] }, { kind: "component", type: FormComponent, selector: "val-form", inputs: ["props"], outputs: ["onSubmit", "onInvalid", "onSelectChange"] }, { kind: "component", type: QrCodeComponent, selector: "val-qr-code", inputs: ["props"], outputs: ["actionComplete", "imageLoad", "imageError"] }, { kind: "component", type: TextComponent, selector: "val-text", inputs: ["props"] }, { kind: "component", type: TitleComponent, selector: "val-title", inputs: ["props"] }, { kind: "component", type: PinInputComponent, selector: "val-pin-input", inputs: ["props"] }] }); }
|
|
35792
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: MfaModalComponent, isStandalone: true, selector: "val-mfa-modal", inputs: { isOpen: "isOpen", prefillCode: "prefillCode" }, outputs: { changed: "changed", enabledViaDeeplink: "enabledViaDeeplink", dismissed: "dismissed" }, ngImport: i0, template: "<ion-modal [isOpen]=\"isOpen\" (didDismiss)=\"close()\" cssClass=\"val-modal-adaptive\">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-buttons slot=\"end\">\n <ion-button fill=\"clear\" color=\"dark\" shape=\"round\" (click)=\"close()\">\n <strong>{{ t('close') }}</strong>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n </ion-header>\n\n <ion-content class=\"ion-padding\">\n <section class=\"mfa-modal\">\n <val-display [props]=\"{ content: t('mfaManageTitle'), size: 'small', color: 'dark' }\" />\n\n @switch (step()) { @case ('loading') {\n <div class=\"mfa-loading\">\n <ion-spinner name=\"crescent\"></ion-spinner>\n </div>\n } @case ('status') { @if (mfaEnabled()) {\n <p class=\"mfa-status mfa-status--on\">{{ t('mfaEnabledLabel') }} \u00B7 {{ methodLabel(mfaMethod()) }}</p>\n\n @if (mfaMethod() === 'TOTP') {\n <div class=\"mfa-block\">\n <val-title [props]=\"{ size: 'small', color: 'dark', bold: true, content: t('mfaBackupCodesTitle') }\" />\n <val-text\n [props]=\"{ size: 'medium', color: 'dark', bold: false, content: t('mfaBackupCodesAvailable') + ': ' + backupCodesCount() }\"\n />\n\n @if (regeneratedCodes(); as codes) {\n <div class=\"mfa-alert\">\n <ion-icon name=\"information-circle-outline\"></ion-icon>\n <p>{{ t('mfaBackupCodesSaveWarning') }} {{ t('mfaBackupCodesExplain') }}</p>\n </div>\n <div class=\"mfa-codes\">\n @for (code of codes; track code) {\n <code class=\"mfa-code\">{{ code }}</code>\n }\n </div>\n <ion-button expand=\"block\" color=\"dark\" fill=\"outline\" shape=\"round\" (click)=\"copyCodes(codes)\">\n {{ t('mfaCopyCodes') }}\n </ion-button>\n } @else { @if (backupCodesCount() < 3) {\n <div class=\"mfa-alert\">\n <ion-icon name=\"information-circle-outline\"></ion-icon>\n <p>{{ t('mfaBackupCodesLow') }}</p>\n </div>\n }\n <ion-button\n expand=\"block\"\n color=\"dark\"\n fill=\"outline\"\n shape=\"round\"\n [disabled]=\"working()\"\n (click)=\"regenerateBackupCodes()\"\n >\n @if (working()) {\n <ion-spinner name=\"crescent\"></ion-spinner>\n } @else { {{ t('mfaRegenerateCodes') }} }\n </ion-button>\n }\n </div>\n }\n\n <ion-button expand=\"block\" color=\"dark\" fill=\"outline\" shape=\"round\" (click)=\"goToDisable()\">\n {{ t('mfaDisableButton') }}\n </ion-button>\n } @else {\n <p class=\"mfa-status mfa-status--off\">{{ t('mfaDisabledLabel') }}</p>\n <val-text [props]=\"{ size: 'medium', color: 'dark', bold: false, content: t('mfaDisabledHint') }\" />\n <ion-button expand=\"block\" shape=\"round\" (click)=\"goToMethodSelect()\"> {{ t('mfaEnableButton') }} </ion-button>\n } } @case ('method-select') {\n <val-title [props]=\"{ content: t('mfaEnableTitle'), size: 'large', color: 'dark', bold: false }\" />\n <val-text [props]=\"{ size: 'medium', color: 'dark', bold: false, content: t('mfaMethodPrompt') }\" />\n\n <div class=\"mfa-method-list\" role=\"radiogroup\">\n <button\n type=\"button\"\n class=\"mfa-method-card\"\n [class.mfa-method-card--active]=\"selectedMethod() === 'TOTP'\"\n (click)=\"selectedMethod.set('TOTP')\"\n role=\"radio\"\n [attr.aria-checked]=\"selectedMethod() === 'TOTP'\"\n >\n <span class=\"mfa-method-card__dot\"></span>\n <span class=\"mfa-method-card__body\">\n <strong>{{ t('mfaMethodTotp') }}</strong>\n <span>{{ t('mfaMethodTotpHint') }}</span>\n </span>\n </button>\n <button\n type=\"button\"\n class=\"mfa-method-card\"\n [class.mfa-method-card--active]=\"selectedMethod() === 'EMAIL'\"\n (click)=\"selectedMethod.set('EMAIL')\"\n role=\"radio\"\n [attr.aria-checked]=\"selectedMethod() === 'EMAIL'\"\n >\n <span class=\"mfa-method-card__dot\"></span>\n <span class=\"mfa-method-card__body\">\n <strong>{{ t('mfaMethodEmail') }}</strong>\n <span>{{ t('mfaMethodEmailHint') }}</span>\n </span>\n </button>\n <button\n type=\"button\"\n class=\"mfa-method-card\"\n [class.mfa-method-card--active]=\"selectedMethod() === 'SMS'\"\n (click)=\"selectedMethod.set('SMS')\"\n role=\"radio\"\n [attr.aria-checked]=\"selectedMethod() === 'SMS'\"\n >\n <span class=\"mfa-method-card__dot\"></span>\n <span class=\"mfa-method-card__body\">\n <strong>{{ t('mfaMethodSms') }}</strong>\n <span>{{ t('mfaMethodSmsHint') }}</span>\n </span>\n </button>\n </div>\n\n @if (selectedMethod() === 'SMS' && !userPhone()) {\n <ion-input\n label=\"{{ t('mfaPhoneLabel') }}\"\n labelPlacement=\"floating\"\n fill=\"outline\"\n type=\"tel\"\n placeholder=\"+56912345678\"\n [formControl]=\"phoneControl\"\n ></ion-input>\n @if (phoneControl.invalid && phoneControl.touched) {\n <p class=\"mfa-error\">{{ t('mfaPhoneInvalid') }}</p>\n } } @if (selectedMethod() === 'SMS' && userPhone(); as phone) {\n <val-text\n [props]=\"{ size: 'medium', color: 'dark', bold: false, content: t('mfaPhoneRegistered') + ': ' + phone }\"\n />\n }\n\n <ion-button expand=\"block\" shape=\"round\" [disabled]=\"working()\" (click)=\"proceedWithMethod()\">\n @if (working()) {\n <ion-spinner name=\"crescent\"></ion-spinner>\n } @else { {{ t('mfaContinue') }} }\n </ion-button>\n <ion-button expand=\"block\" fill=\"clear\" color=\"dark\" shape=\"round\" (click)=\"backToStatus()\">\n {{ t('mfaCancel') }}\n </ion-button>\n } @case ('totp-setup') {\n <val-title [props]=\"{ content: t('mfaTotpSetupTitle'), size: 'large', color: '', bold: false }\" />\n <val-text [props]=\"{ size: 'medium', color: 'dark', bold: false, content: t('mfaTotpStep1') }\" />\n\n @if (totpQr(); as qr) {\n <div class=\"mfa-qr\">\n <val-qr-code [props]=\"{ qr: qr }\" />\n </div>\n } @if (totpSetup(); as setup) {\n <val-text [props]=\"{ size: 'medium', color: 'dark', bold: false, content: t('mfaTotpManualEntry') }\" />\n <div class=\"mfa-secret-row\">\n <code class=\"mfa-secret\">{{ setup.secret }}</code>\n <ion-button fill=\"clear\" size=\"small\" (click)=\"copySecret(setup.secret)\" [attr.aria-label]=\"t('copy')\">\n @if (copiedSecret()) {\n <ion-icon name=\"checkmark-outline\"></ion-icon>\n } @else {\n <ion-icon name=\"copy-outline\"></ion-icon>\n }\n </ion-button>\n </div>\n\n <val-text [props]=\"{ size: 'medium', color: 'dark', bold: false, content: t('mfaTotpStep2') }\" />\n <div class=\"mfa-pin\">\n <val-pin-input [props]=\"pinInputProps\" />\n </div>\n\n <ion-button expand=\"block\" shape=\"round\" [disabled]=\"working()\" (click)=\"verifyTotp()\">\n @if (working()) {\n <ion-spinner name=\"crescent\"></ion-spinner>\n } @else { {{ t('mfaTotpVerify') }} }\n </ion-button>\n\n <div class=\"mfa-backup\">\n <val-title [props]=\"{ size: 'small', color: 'dark', bold: true, content: t('mfaBackupCodesTitle') }\" />\n <div class=\"mfa-alert\">\n <ion-icon name=\"information-circle-outline\"></ion-icon>\n <p>{{ t('mfaBackupCodesSaveWarning') }} {{ t('mfaBackupCodesExplain') }}</p>\n </div>\n <div class=\"mfa-codes\">\n @for (code of setup.backupCodes; track code) {\n <code class=\"mfa-code\">{{ code }}</code>\n }\n </div>\n <ion-button expand=\"block\" color=\"dark\" fill=\"outline\" shape=\"round\" (click)=\"copyCodes(setup.backupCodes)\">\n {{ t('mfaCopyCodes') }}\n </ion-button>\n </div>\n }\n\n <ion-button expand=\"block\" fill=\"clear\" color=\"dark\" shape=\"round\" (click)=\"backToStatus()\">\n {{ t('mfaCancel') }}\n </ion-button>\n } @case ('code-confirm') {\n <val-title [props]=\"{ content: t('mfaConfirmTitle'), size: 'large', color: '', bold: false }\" />\n <val-text\n [props]=\"{ size: 'medium', color: 'dark', bold: false, content: selectedMethod() === 'EMAIL' ? t('mfaConfirmPromptEmail') : t('mfaConfirmPromptSms') }\"\n />\n\n <div class=\"mfa-pin\">\n <val-pin-input [props]=\"pinInputProps\" />\n </div>\n\n <ion-button expand=\"block\" shape=\"round\" [disabled]=\"working()\" (click)=\"confirmCode()\">\n @if (working()) {\n <ion-spinner name=\"crescent\"></ion-spinner>\n } @else { {{ t('mfaConfirmButton') }} }\n </ion-button>\n\n <p class=\"mfa-resend\">\n {{ t('mfaNoCode') }} @if (resendCooldown() > 0) {\n <span>{{ t('mfaResendIn') }} {{ resendCooldown() }}s</span>\n } @else {\n <a (click)=\"resendCode()\">{{ t('mfaResend') }}</a>\n }\n </p>\n\n <ion-button expand=\"block\" fill=\"clear\" color=\"dark\" shape=\"round\" (click)=\"backToStatus()\">\n {{ t('mfaCancel') }}\n </ion-button>\n } @case ('disable') { @if (hasPassword()) {\n <val-form [props]=\"disableFormProps()\" (onSubmit)=\"onDisableSubmit($event)\" />\n } @else if (mfaMethod() === 'TOTP') {\n <val-title [props]=\"{ content: t('mfaDisableTitle'), size: 'large', color: '', bold: false }\" />\n <val-text [props]=\"{ size: 'medium', color: 'dark', bold: false, content: t('mfaDisableTotpPrompt') }\" />\n <div class=\"mfa-pin\">\n <val-pin-input [props]=\"pinInputProps\" />\n </div>\n <ion-button expand=\"block\" shape=\"round\" [disabled]=\"working()\" (click)=\"disableWithMfaCode()\">\n @if (working()) {\n <ion-spinner name=\"crescent\"></ion-spinner>\n } @else { {{ t('mfaDisableButton') }} }\n </ion-button>\n } @else if (mfaMethod() === 'EMAIL' || mfaMethod() === 'SMS') {\n <val-title [props]=\"{ content: t('mfaDisableTitle'), size: 'large', color: '', bold: false }\" />\n @if (!disableCodeSent()) {\n <val-text [props]=\"{ size: 'medium', color: 'dark', bold: false, content: t('mfaDisableCodePrompt') }\" />\n <ion-button expand=\"block\" shape=\"round\" [disabled]=\"working()\" (click)=\"sendDisableCode()\">\n @if (working()) {\n <ion-spinner name=\"crescent\"></ion-spinner>\n } @else { {{ t('mfaDisableSendCode') }} }\n </ion-button>\n } @else {\n <val-text [props]=\"{ size: 'medium', color: 'dark', bold: false, content: t('mfaDisableCodePrompt') }\" />\n <div class=\"mfa-pin\">\n <val-pin-input [props]=\"pinInputProps\" />\n </div>\n <ion-button expand=\"block\" shape=\"round\" [disabled]=\"working()\" (click)=\"disableWithMfaCode()\">\n @if (working()) {\n <ion-spinner name=\"crescent\"></ion-spinner>\n } @else { {{ t('mfaDisableButton') }} }\n </ion-button>\n <ion-button expand=\"block\" fill=\"clear\" color=\"medium\" shape=\"round\" [disabled]=\"working()\" (click)=\"sendDisableCode()\">\n {{ t('mfaDisableResendCode') }}\n </ion-button>\n } } @else {\n <val-title [props]=\"{ content: t('mfaDisableTitle'), size: 'large', color: '', bold: false }\" />\n <val-text [props]=\"{ size: 'medium', color: 'dark', bold: false, content: t('mfaDisableNeedsPassword') }\" />\n }\n <ion-button expand=\"block\" fill=\"clear\" color=\"dark\" shape=\"round\" (click)=\"backToStatus()\">\n {{ t('mfaCancel') }}\n </ion-button>\n } }\n </section>\n </ion-content>\n </ng-template>\n</ion-modal>\n", styles: [".mfa-modal{display:flex;flex-direction:column;gap:14px;padding-top:4px}.mfa-loading{display:flex;justify-content:center;padding:40px 0}.mfa-status{display:inline-flex;align-items:center;width:fit-content;padding:4px 10px;border-radius:999px;font-size:.8125rem;font-weight:600;margin:0}.mfa-status--on{color:var(--ion-color-success-shade);background:rgba(var(--ion-color-success-rgb),.12)}.mfa-status--off{color:var(--ion-color-medium-shade);background:var(--ion-color-light)}.mfa-secret-row{display:flex;align-items:center;gap:8px}.mfa-secret-row .mfa-secret{flex:1;margin:0}.mfa-secret-row ion-button{flex-shrink:0;--color: var(--ion-color-dark);margin:0}.mfa-error{color:var(--ion-color-danger);font-size:.8125rem;margin:4px 0 0}.mfa-block,.mfa-backup{display:flex;flex-direction:column;gap:12px}.mfa-block{padding:16px;border-radius:14px;background:var(--ion-color-light)}.mfa-alert{display:flex;align-items:flex-start;gap:10px;padding:12px 14px;border-radius:10px;background:var(--ion-color-light);border-left:3px solid var(--ion-color-primary)}.mfa-alert ion-icon{font-size:1.25rem;color:var(--ion-color-primary);flex-shrink:0;margin-top:1px}.mfa-alert p{margin:0;font-size:.8125rem;line-height:1.45;color:var(--ion-color-dark)}.mfa-qr{display:flex;justify-content:center;padding:8px 0}.mfa-secret{display:block;padding:10px 12px;border-radius:8px;background:var(--ion-color-light);font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.9375rem;letter-spacing:.04em;word-break:break-all;text-align:center}.mfa-pin{display:flex;justify-content:center;padding:8px 0}.mfa-codes{display:grid;grid-template-columns:repeat(2,1fr);gap:8px;padding:12px;border-radius:12px;background:var(--ion-color-light)}.mfa-code{padding:8px;border-radius:6px;background:var(--ion-background-color, #fff);color:var(--ion-color-dark);font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.8125rem;text-align:center}.mfa-method-list{display:flex;flex-direction:column;gap:8px}.mfa-method-card{display:flex;align-items:flex-start;gap:12px;padding:14px 16px;border-radius:12px;border:1.5px solid var(--ion-border-color, rgba(var(--ion-color-dark-rgb), .14));background:transparent;cursor:pointer;text-align:left;width:100%;transition:border-color .15s ease,background .15s ease}.mfa-method-card--active{border-color:var(--ion-color-primary);background:rgba(var(--ion-color-primary-rgb),.07)}.mfa-method-card__dot{flex-shrink:0;width:18px;height:18px;border-radius:50%;border:2px solid var(--ion-color-medium);margin-top:2px;transition:border-color .15s,box-shadow .15s}.mfa-method-card--active .mfa-method-card__dot{border-color:var(--ion-color-primary);box-shadow:inset 0 0 0 4px var(--ion-color-primary)}.mfa-method-card__body{display:flex;flex-direction:column;gap:3px}.mfa-method-card__body strong{font-size:.9375rem;font-weight:600;color:var(--ion-color-dark)}.mfa-method-card__body span{font-size:.8125rem;color:var(--ion-color-medium);line-height:1.4}.mfa-resend{text-align:center;font-size:.875rem;color:var(--ion-color-dark);margin:4px 0}.mfa-resend a{color:var(--ion-color-primary);cursor:pointer;font-weight:600}\n"], dependencies: [{ 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: IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: IonInput, selector: "ion-input", inputs: ["accept", "autocapitalize", "autocomplete", "autocorrect", "autofocus", "clearInput", "clearOnEdit", "color", "counter", "counterFormatter", "debounce", "disabled", "enterkeyhint", "errorText", "fill", "helperText", "inputmode", "label", "labelPlacement", "max", "maxlength", "min", "minlength", "mode", "multiple", "name", "pattern", "placeholder", "readonly", "required", "shape", "size", "spellcheck", "step", "type", "value"] }, { kind: "component", type: IonModal, selector: "ion-modal" }, { kind: "component", type: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { kind: "component", type: IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "component", type: DisplayComponent, selector: "val-display", inputs: ["props"] }, { kind: "component", type: FormComponent, selector: "val-form", inputs: ["props"], outputs: ["onSubmit", "onInvalid", "onSelectChange"] }, { kind: "component", type: QrCodeComponent, selector: "val-qr-code", inputs: ["props"], outputs: ["actionComplete", "imageLoad", "imageError"] }, { kind: "component", type: TextComponent, selector: "val-text", inputs: ["props"] }, { kind: "component", type: TitleComponent, selector: "val-title", inputs: ["props"] }, { kind: "component", type: PinInputComponent, selector: "val-pin-input", inputs: ["props"] }] }); }
|
|
35793
35793
|
}
|
|
35794
35794
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MfaModalComponent, decorators: [{
|
|
35795
35795
|
type: Component,
|
|
@@ -35814,7 +35814,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
35814
35814
|
TextComponent,
|
|
35815
35815
|
TitleComponent,
|
|
35816
35816
|
PinInputComponent,
|
|
35817
|
-
], template: "<ion-modal [isOpen]=\"isOpen\" (didDismiss)=\"close()\" cssClass=\"val-modal-adaptive\">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-buttons slot=\"end\">\n <ion-button fill=\"clear\" color=\"dark\" (click)=\"close()\">\n <strong>{{ t('close') }}</strong>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n </ion-header>\n\n <ion-content class=\"ion-padding\">\n <section class=\"mfa-modal\">\n <val-display [props]=\"{ content: t('mfaManageTitle'), size: 'small', color: 'dark' }\" />\n\n @switch (step()) { @case ('loading') {\n <div class=\"mfa-loading\">\n <ion-spinner name=\"crescent\"></ion-spinner>\n </div>\n } @case ('status') { @if (mfaEnabled()) {\n <p class=\"mfa-status mfa-status--on\">{{ t('mfaEnabledLabel') }} \u00B7 {{ methodLabel(mfaMethod()) }}</p>\n\n @if (mfaMethod() === 'TOTP') {\n <div class=\"mfa-block\">\n <val-title [props]=\"{ size: 'small', color: 'dark', bold: true, content: t('mfaBackupCodesTitle') }\" />\n <val-text\n [props]=\"{ size: 'medium', color: 'dark', bold: false, content: t('mfaBackupCodesAvailable') + ': ' + backupCodesCount() }\"\n />\n\n @if (regeneratedCodes(); as codes) {\n <div class=\"mfa-alert\">\n <ion-icon name=\"information-circle-outline\"></ion-icon>\n <p>{{ t('mfaBackupCodesSaveWarning') }} {{ t('mfaBackupCodesExplain') }}</p>\n </div>\n <div class=\"mfa-codes\">\n @for (code of codes; track code) {\n <code class=\"mfa-code\">{{ code }}</code>\n }\n </div>\n <ion-button expand=\"block\" color=\"dark\" fill=\"outline\" (click)=\"copyCodes(codes)\">\n {{ t('mfaCopyCodes') }}\n </ion-button>\n } @else { @if (backupCodesCount() < 3) {\n <div class=\"mfa-alert\">\n <ion-icon name=\"information-circle-outline\"></ion-icon>\n <p>{{ t('mfaBackupCodesLow') }}</p>\n </div>\n }\n <ion-button\n expand=\"block\"\n color=\"dark\"\n fill=\"outline\"\n [disabled]=\"working()\"\n (click)=\"regenerateBackupCodes()\"\n >\n @if (working()) {\n <ion-spinner name=\"crescent\"></ion-spinner>\n } @else { {{ t('mfaRegenerateCodes') }} }\n </ion-button>\n }\n </div>\n }\n\n <ion-button expand=\"block\" color=\"dark\" fill=\"outline\" (click)=\"goToDisable()\">\n {{ t('mfaDisableButton') }}\n </ion-button>\n } @else {\n <p class=\"mfa-status mfa-status--off\">{{ t('mfaDisabledLabel') }}</p>\n <val-text [props]=\"{ size: 'medium', color: 'dark', bold: false, content: t('mfaDisabledHint') }\" />\n <ion-button expand=\"block\" (click)=\"goToMethodSelect()\"> {{ t('mfaEnableButton') }} </ion-button>\n } } @case ('method-select') {\n <val-title [props]=\"{ content: t('mfaEnableTitle'), size: 'large', color: 'dark', bold: false }\" />\n <val-text [props]=\"{ size: 'medium', color: 'dark', bold: false, content: t('mfaMethodPrompt') }\" />\n\n <div class=\"mfa-method-list\" role=\"radiogroup\">\n <button\n type=\"button\"\n class=\"mfa-method-card\"\n [class.mfa-method-card--active]=\"selectedMethod() === 'TOTP'\"\n (click)=\"selectedMethod.set('TOTP')\"\n role=\"radio\"\n [attr.aria-checked]=\"selectedMethod() === 'TOTP'\"\n >\n <span class=\"mfa-method-card__dot\"></span>\n <span class=\"mfa-method-card__body\">\n <strong>{{ t('mfaMethodTotp') }}</strong>\n <span>{{ t('mfaMethodTotpHint') }}</span>\n </span>\n </button>\n <button\n type=\"button\"\n class=\"mfa-method-card\"\n [class.mfa-method-card--active]=\"selectedMethod() === 'EMAIL'\"\n (click)=\"selectedMethod.set('EMAIL')\"\n role=\"radio\"\n [attr.aria-checked]=\"selectedMethod() === 'EMAIL'\"\n >\n <span class=\"mfa-method-card__dot\"></span>\n <span class=\"mfa-method-card__body\">\n <strong>{{ t('mfaMethodEmail') }}</strong>\n <span>{{ t('mfaMethodEmailHint') }}</span>\n </span>\n </button>\n <button\n type=\"button\"\n class=\"mfa-method-card\"\n [class.mfa-method-card--active]=\"selectedMethod() === 'SMS'\"\n (click)=\"selectedMethod.set('SMS')\"\n role=\"radio\"\n [attr.aria-checked]=\"selectedMethod() === 'SMS'\"\n >\n <span class=\"mfa-method-card__dot\"></span>\n <span class=\"mfa-method-card__body\">\n <strong>{{ t('mfaMethodSms') }}</strong>\n <span>{{ t('mfaMethodSmsHint') }}</span>\n </span>\n </button>\n </div>\n\n @if (selectedMethod() === 'SMS' && !userPhone()) {\n <ion-input\n label=\"{{ t('mfaPhoneLabel') }}\"\n labelPlacement=\"floating\"\n fill=\"outline\"\n type=\"tel\"\n placeholder=\"+56912345678\"\n [formControl]=\"phoneControl\"\n ></ion-input>\n @if (phoneControl.invalid && phoneControl.touched) {\n <p class=\"mfa-error\">{{ t('mfaPhoneInvalid') }}</p>\n } } @if (selectedMethod() === 'SMS' && userPhone(); as phone) {\n <val-text\n [props]=\"{ size: 'medium', color: 'dark', bold: false, content: t('mfaPhoneRegistered') + ': ' + phone }\"\n />\n }\n\n <ion-button expand=\"block\" [disabled]=\"working()\" (click)=\"proceedWithMethod()\">\n @if (working()) {\n <ion-spinner name=\"crescent\"></ion-spinner>\n } @else { {{ t('mfaContinue') }} }\n </ion-button>\n <ion-button expand=\"block\" fill=\"clear\" color=\"dark\" (click)=\"backToStatus()\">\n {{ t('mfaCancel') }}\n </ion-button>\n } @case ('totp-setup') {\n <val-title [props]=\"{ content: t('mfaTotpSetupTitle'), size: 'large', color: '', bold: false }\" />\n <val-text [props]=\"{ size: 'medium', color: 'dark', bold: false, content: t('mfaTotpStep1') }\" />\n\n @if (totpQr(); as qr) {\n <div class=\"mfa-qr\">\n <val-qr-code [props]=\"{ qr: qr }\" />\n </div>\n } @if (totpSetup(); as setup) {\n <val-text [props]=\"{ size: 'medium', color: 'dark', bold: false, content: t('mfaTotpManualEntry') }\" />\n <div class=\"mfa-secret-row\">\n <code class=\"mfa-secret\">{{ setup.secret }}</code>\n <ion-button fill=\"clear\" size=\"small\" (click)=\"copySecret(setup.secret)\" [attr.aria-label]=\"t('copy')\">\n @if (copiedSecret()) {\n <ion-icon name=\"checkmark-outline\"></ion-icon>\n } @else {\n <ion-icon name=\"copy-outline\"></ion-icon>\n }\n </ion-button>\n </div>\n\n <val-text [props]=\"{ size: 'medium', color: 'dark', bold: false, content: t('mfaTotpStep2') }\" />\n <div class=\"mfa-pin\">\n <val-pin-input [props]=\"pinInputProps\" />\n </div>\n\n <ion-button expand=\"block\" [disabled]=\"working()\" (click)=\"verifyTotp()\">\n @if (working()) {\n <ion-spinner name=\"crescent\"></ion-spinner>\n } @else { {{ t('mfaTotpVerify') }} }\n </ion-button>\n\n <div class=\"mfa-backup\">\n <val-title [props]=\"{ size: 'small', color: 'dark', bold: true, content: t('mfaBackupCodesTitle') }\" />\n <div class=\"mfa-alert\">\n <ion-icon name=\"information-circle-outline\"></ion-icon>\n <p>{{ t('mfaBackupCodesSaveWarning') }} {{ t('mfaBackupCodesExplain') }}</p>\n </div>\n <div class=\"mfa-codes\">\n @for (code of setup.backupCodes; track code) {\n <code class=\"mfa-code\">{{ code }}</code>\n }\n </div>\n <ion-button expand=\"block\" color=\"dark\" fill=\"outline\" (click)=\"copyCodes(setup.backupCodes)\">\n {{ t('mfaCopyCodes') }}\n </ion-button>\n </div>\n }\n\n <ion-button expand=\"block\" fill=\"clear\" color=\"dark\" (click)=\"backToStatus()\">\n {{ t('mfaCancel') }}\n </ion-button>\n } @case ('code-confirm') {\n <val-title [props]=\"{ content: t('mfaConfirmTitle'), size: 'large', color: '', bold: false }\" />\n <val-text\n [props]=\"{ size: 'medium', color: 'dark', bold: false, content: selectedMethod() === 'EMAIL' ? t('mfaConfirmPromptEmail') : t('mfaConfirmPromptSms') }\"\n />\n\n <div class=\"mfa-pin\">\n <val-pin-input [props]=\"pinInputProps\" />\n </div>\n\n <ion-button expand=\"block\" [disabled]=\"working()\" (click)=\"confirmCode()\">\n @if (working()) {\n <ion-spinner name=\"crescent\"></ion-spinner>\n } @else { {{ t('mfaConfirmButton') }} }\n </ion-button>\n\n <p class=\"mfa-resend\">\n {{ t('mfaNoCode') }} @if (resendCooldown() > 0) {\n <span>{{ t('mfaResendIn') }} {{ resendCooldown() }}s</span>\n } @else {\n <a (click)=\"resendCode()\">{{ t('mfaResend') }}</a>\n }\n </p>\n\n <ion-button expand=\"block\" fill=\"clear\" color=\"dark\" (click)=\"backToStatus()\">\n {{ t('mfaCancel') }}\n </ion-button>\n } @case ('disable') { @if (hasPassword()) {\n <val-form [props]=\"disableFormProps()\" (onSubmit)=\"onDisableSubmit($event)\" />\n } @else if (mfaMethod() === 'TOTP') {\n <val-title [props]=\"{ content: t('mfaDisableTitle'), size: 'large', color: '', bold: false }\" />\n <val-text [props]=\"{ size: 'medium', color: 'dark', bold: false, content: t('mfaDisableTotpPrompt') }\" />\n <div class=\"mfa-pin\">\n <val-pin-input [props]=\"pinInputProps\" />\n </div>\n <ion-button expand=\"block\" [disabled]=\"working()\" (click)=\"disableWithMfaCode()\">\n @if (working()) {\n <ion-spinner name=\"crescent\"></ion-spinner>\n } @else { {{ t('mfaDisableButton') }} }\n </ion-button>\n } @else if (mfaMethod() === 'EMAIL' || mfaMethod() === 'SMS') {\n <val-title [props]=\"{ content: t('mfaDisableTitle'), size: 'large', color: '', bold: false }\" />\n @if (!disableCodeSent()) {\n <val-text [props]=\"{ size: 'medium', color: 'dark', bold: false, content: t('mfaDisableCodePrompt') }\" />\n <ion-button expand=\"block\" [disabled]=\"working()\" (click)=\"sendDisableCode()\">\n @if (working()) {\n <ion-spinner name=\"crescent\"></ion-spinner>\n } @else { {{ t('mfaDisableSendCode') }} }\n </ion-button>\n } @else {\n <val-text [props]=\"{ size: 'medium', color: 'dark', bold: false, content: t('mfaDisableCodePrompt') }\" />\n <div class=\"mfa-pin\">\n <val-pin-input [props]=\"pinInputProps\" />\n </div>\n <ion-button expand=\"block\" [disabled]=\"working()\" (click)=\"disableWithMfaCode()\">\n @if (working()) {\n <ion-spinner name=\"crescent\"></ion-spinner>\n } @else { {{ t('mfaDisableButton') }} }\n </ion-button>\n <ion-button expand=\"block\" fill=\"clear\" color=\"medium\" [disabled]=\"working()\" (click)=\"sendDisableCode()\">\n {{ t('mfaDisableResendCode') }}\n </ion-button>\n } } @else {\n <val-title [props]=\"{ content: t('mfaDisableTitle'), size: 'large', color: '', bold: false }\" />\n <val-text [props]=\"{ size: 'medium', color: 'dark', bold: false, content: t('mfaDisableNeedsPassword') }\" />\n }\n <ion-button expand=\"block\" fill=\"clear\" color=\"dark\" (click)=\"backToStatus()\">\n {{ t('mfaCancel') }}\n </ion-button>\n } }\n </section>\n </ion-content>\n </ng-template>\n</ion-modal>\n", styles: [".mfa-modal{display:flex;flex-direction:column;gap:14px;padding-top:4px}.mfa-loading{display:flex;justify-content:center;padding:40px 0}.mfa-status{display:inline-flex;align-items:center;width:fit-content;padding:4px 10px;border-radius:999px;font-size:.8125rem;font-weight:600;margin:0}.mfa-status--on{color:var(--ion-color-success-shade);background:rgba(var(--ion-color-success-rgb),.12)}.mfa-status--off{color:var(--ion-color-medium-shade);background:var(--ion-color-light)}.mfa-secret-row{display:flex;align-items:center;gap:8px}.mfa-secret-row .mfa-secret{flex:1;margin:0}.mfa-secret-row ion-button{flex-shrink:0;--color: var(--ion-color-dark);margin:0}.mfa-error{color:var(--ion-color-danger);font-size:.8125rem;margin:4px 0 0}.mfa-block,.mfa-backup{display:flex;flex-direction:column;gap:12px}.mfa-block{padding:16px;border-radius:14px;background:var(--ion-color-light)}.mfa-alert{display:flex;align-items:flex-start;gap:10px;padding:12px 14px;border-radius:10px;background:var(--ion-color-light);border-left:3px solid var(--ion-color-primary)}.mfa-alert ion-icon{font-size:1.25rem;color:var(--ion-color-primary);flex-shrink:0;margin-top:1px}.mfa-alert p{margin:0;font-size:.8125rem;line-height:1.45;color:var(--ion-color-dark)}.mfa-qr{display:flex;justify-content:center;padding:8px 0}.mfa-secret{display:block;padding:10px 12px;border-radius:8px;background:var(--ion-color-light);font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.9375rem;letter-spacing:.04em;word-break:break-all;text-align:center}.mfa-pin{display:flex;justify-content:center;padding:8px 0}.mfa-codes{display:grid;grid-template-columns:repeat(2,1fr);gap:8px;padding:12px;border-radius:12px;background:var(--ion-color-light)}.mfa-code{padding:8px;border-radius:6px;background:var(--ion-background-color, #fff);color:var(--ion-color-dark);font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.8125rem;text-align:center}.mfa-method-list{display:flex;flex-direction:column;gap:8px}.mfa-method-card{display:flex;align-items:flex-start;gap:12px;padding:14px 16px;border-radius:12px;border:1.5px solid var(--ion-border-color, rgba(var(--ion-color-dark-rgb), .14));background:transparent;cursor:pointer;text-align:left;width:100%;transition:border-color .15s ease,background .15s ease}.mfa-method-card--active{border-color:var(--ion-color-primary);background:rgba(var(--ion-color-primary-rgb),.07)}.mfa-method-card__dot{flex-shrink:0;width:18px;height:18px;border-radius:50%;border:2px solid var(--ion-color-medium);margin-top:2px;transition:border-color .15s,box-shadow .15s}.mfa-method-card--active .mfa-method-card__dot{border-color:var(--ion-color-primary);box-shadow:inset 0 0 0 4px var(--ion-color-primary)}.mfa-method-card__body{display:flex;flex-direction:column;gap:3px}.mfa-method-card__body strong{font-size:.9375rem;font-weight:600;color:var(--ion-color-dark)}.mfa-method-card__body span{font-size:.8125rem;color:var(--ion-color-medium);line-height:1.4}.mfa-resend{text-align:center;font-size:.875rem;color:var(--ion-color-dark);margin:4px 0}.mfa-resend a{color:var(--ion-color-primary);cursor:pointer;font-weight:600}\n"] }]
|
|
35817
|
+
], template: "<ion-modal [isOpen]=\"isOpen\" (didDismiss)=\"close()\" cssClass=\"val-modal-adaptive\">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-buttons slot=\"end\">\n <ion-button fill=\"clear\" color=\"dark\" shape=\"round\" (click)=\"close()\">\n <strong>{{ t('close') }}</strong>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n </ion-header>\n\n <ion-content class=\"ion-padding\">\n <section class=\"mfa-modal\">\n <val-display [props]=\"{ content: t('mfaManageTitle'), size: 'small', color: 'dark' }\" />\n\n @switch (step()) { @case ('loading') {\n <div class=\"mfa-loading\">\n <ion-spinner name=\"crescent\"></ion-spinner>\n </div>\n } @case ('status') { @if (mfaEnabled()) {\n <p class=\"mfa-status mfa-status--on\">{{ t('mfaEnabledLabel') }} \u00B7 {{ methodLabel(mfaMethod()) }}</p>\n\n @if (mfaMethod() === 'TOTP') {\n <div class=\"mfa-block\">\n <val-title [props]=\"{ size: 'small', color: 'dark', bold: true, content: t('mfaBackupCodesTitle') }\" />\n <val-text\n [props]=\"{ size: 'medium', color: 'dark', bold: false, content: t('mfaBackupCodesAvailable') + ': ' + backupCodesCount() }\"\n />\n\n @if (regeneratedCodes(); as codes) {\n <div class=\"mfa-alert\">\n <ion-icon name=\"information-circle-outline\"></ion-icon>\n <p>{{ t('mfaBackupCodesSaveWarning') }} {{ t('mfaBackupCodesExplain') }}</p>\n </div>\n <div class=\"mfa-codes\">\n @for (code of codes; track code) {\n <code class=\"mfa-code\">{{ code }}</code>\n }\n </div>\n <ion-button expand=\"block\" color=\"dark\" fill=\"outline\" shape=\"round\" (click)=\"copyCodes(codes)\">\n {{ t('mfaCopyCodes') }}\n </ion-button>\n } @else { @if (backupCodesCount() < 3) {\n <div class=\"mfa-alert\">\n <ion-icon name=\"information-circle-outline\"></ion-icon>\n <p>{{ t('mfaBackupCodesLow') }}</p>\n </div>\n }\n <ion-button\n expand=\"block\"\n color=\"dark\"\n fill=\"outline\"\n shape=\"round\"\n [disabled]=\"working()\"\n (click)=\"regenerateBackupCodes()\"\n >\n @if (working()) {\n <ion-spinner name=\"crescent\"></ion-spinner>\n } @else { {{ t('mfaRegenerateCodes') }} }\n </ion-button>\n }\n </div>\n }\n\n <ion-button expand=\"block\" color=\"dark\" fill=\"outline\" shape=\"round\" (click)=\"goToDisable()\">\n {{ t('mfaDisableButton') }}\n </ion-button>\n } @else {\n <p class=\"mfa-status mfa-status--off\">{{ t('mfaDisabledLabel') }}</p>\n <val-text [props]=\"{ size: 'medium', color: 'dark', bold: false, content: t('mfaDisabledHint') }\" />\n <ion-button expand=\"block\" shape=\"round\" (click)=\"goToMethodSelect()\"> {{ t('mfaEnableButton') }} </ion-button>\n } } @case ('method-select') {\n <val-title [props]=\"{ content: t('mfaEnableTitle'), size: 'large', color: 'dark', bold: false }\" />\n <val-text [props]=\"{ size: 'medium', color: 'dark', bold: false, content: t('mfaMethodPrompt') }\" />\n\n <div class=\"mfa-method-list\" role=\"radiogroup\">\n <button\n type=\"button\"\n class=\"mfa-method-card\"\n [class.mfa-method-card--active]=\"selectedMethod() === 'TOTP'\"\n (click)=\"selectedMethod.set('TOTP')\"\n role=\"radio\"\n [attr.aria-checked]=\"selectedMethod() === 'TOTP'\"\n >\n <span class=\"mfa-method-card__dot\"></span>\n <span class=\"mfa-method-card__body\">\n <strong>{{ t('mfaMethodTotp') }}</strong>\n <span>{{ t('mfaMethodTotpHint') }}</span>\n </span>\n </button>\n <button\n type=\"button\"\n class=\"mfa-method-card\"\n [class.mfa-method-card--active]=\"selectedMethod() === 'EMAIL'\"\n (click)=\"selectedMethod.set('EMAIL')\"\n role=\"radio\"\n [attr.aria-checked]=\"selectedMethod() === 'EMAIL'\"\n >\n <span class=\"mfa-method-card__dot\"></span>\n <span class=\"mfa-method-card__body\">\n <strong>{{ t('mfaMethodEmail') }}</strong>\n <span>{{ t('mfaMethodEmailHint') }}</span>\n </span>\n </button>\n <button\n type=\"button\"\n class=\"mfa-method-card\"\n [class.mfa-method-card--active]=\"selectedMethod() === 'SMS'\"\n (click)=\"selectedMethod.set('SMS')\"\n role=\"radio\"\n [attr.aria-checked]=\"selectedMethod() === 'SMS'\"\n >\n <span class=\"mfa-method-card__dot\"></span>\n <span class=\"mfa-method-card__body\">\n <strong>{{ t('mfaMethodSms') }}</strong>\n <span>{{ t('mfaMethodSmsHint') }}</span>\n </span>\n </button>\n </div>\n\n @if (selectedMethod() === 'SMS' && !userPhone()) {\n <ion-input\n label=\"{{ t('mfaPhoneLabel') }}\"\n labelPlacement=\"floating\"\n fill=\"outline\"\n type=\"tel\"\n placeholder=\"+56912345678\"\n [formControl]=\"phoneControl\"\n ></ion-input>\n @if (phoneControl.invalid && phoneControl.touched) {\n <p class=\"mfa-error\">{{ t('mfaPhoneInvalid') }}</p>\n } } @if (selectedMethod() === 'SMS' && userPhone(); as phone) {\n <val-text\n [props]=\"{ size: 'medium', color: 'dark', bold: false, content: t('mfaPhoneRegistered') + ': ' + phone }\"\n />\n }\n\n <ion-button expand=\"block\" shape=\"round\" [disabled]=\"working()\" (click)=\"proceedWithMethod()\">\n @if (working()) {\n <ion-spinner name=\"crescent\"></ion-spinner>\n } @else { {{ t('mfaContinue') }} }\n </ion-button>\n <ion-button expand=\"block\" fill=\"clear\" color=\"dark\" shape=\"round\" (click)=\"backToStatus()\">\n {{ t('mfaCancel') }}\n </ion-button>\n } @case ('totp-setup') {\n <val-title [props]=\"{ content: t('mfaTotpSetupTitle'), size: 'large', color: '', bold: false }\" />\n <val-text [props]=\"{ size: 'medium', color: 'dark', bold: false, content: t('mfaTotpStep1') }\" />\n\n @if (totpQr(); as qr) {\n <div class=\"mfa-qr\">\n <val-qr-code [props]=\"{ qr: qr }\" />\n </div>\n } @if (totpSetup(); as setup) {\n <val-text [props]=\"{ size: 'medium', color: 'dark', bold: false, content: t('mfaTotpManualEntry') }\" />\n <div class=\"mfa-secret-row\">\n <code class=\"mfa-secret\">{{ setup.secret }}</code>\n <ion-button fill=\"clear\" size=\"small\" (click)=\"copySecret(setup.secret)\" [attr.aria-label]=\"t('copy')\">\n @if (copiedSecret()) {\n <ion-icon name=\"checkmark-outline\"></ion-icon>\n } @else {\n <ion-icon name=\"copy-outline\"></ion-icon>\n }\n </ion-button>\n </div>\n\n <val-text [props]=\"{ size: 'medium', color: 'dark', bold: false, content: t('mfaTotpStep2') }\" />\n <div class=\"mfa-pin\">\n <val-pin-input [props]=\"pinInputProps\" />\n </div>\n\n <ion-button expand=\"block\" shape=\"round\" [disabled]=\"working()\" (click)=\"verifyTotp()\">\n @if (working()) {\n <ion-spinner name=\"crescent\"></ion-spinner>\n } @else { {{ t('mfaTotpVerify') }} }\n </ion-button>\n\n <div class=\"mfa-backup\">\n <val-title [props]=\"{ size: 'small', color: 'dark', bold: true, content: t('mfaBackupCodesTitle') }\" />\n <div class=\"mfa-alert\">\n <ion-icon name=\"information-circle-outline\"></ion-icon>\n <p>{{ t('mfaBackupCodesSaveWarning') }} {{ t('mfaBackupCodesExplain') }}</p>\n </div>\n <div class=\"mfa-codes\">\n @for (code of setup.backupCodes; track code) {\n <code class=\"mfa-code\">{{ code }}</code>\n }\n </div>\n <ion-button expand=\"block\" color=\"dark\" fill=\"outline\" shape=\"round\" (click)=\"copyCodes(setup.backupCodes)\">\n {{ t('mfaCopyCodes') }}\n </ion-button>\n </div>\n }\n\n <ion-button expand=\"block\" fill=\"clear\" color=\"dark\" shape=\"round\" (click)=\"backToStatus()\">\n {{ t('mfaCancel') }}\n </ion-button>\n } @case ('code-confirm') {\n <val-title [props]=\"{ content: t('mfaConfirmTitle'), size: 'large', color: '', bold: false }\" />\n <val-text\n [props]=\"{ size: 'medium', color: 'dark', bold: false, content: selectedMethod() === 'EMAIL' ? t('mfaConfirmPromptEmail') : t('mfaConfirmPromptSms') }\"\n />\n\n <div class=\"mfa-pin\">\n <val-pin-input [props]=\"pinInputProps\" />\n </div>\n\n <ion-button expand=\"block\" shape=\"round\" [disabled]=\"working()\" (click)=\"confirmCode()\">\n @if (working()) {\n <ion-spinner name=\"crescent\"></ion-spinner>\n } @else { {{ t('mfaConfirmButton') }} }\n </ion-button>\n\n <p class=\"mfa-resend\">\n {{ t('mfaNoCode') }} @if (resendCooldown() > 0) {\n <span>{{ t('mfaResendIn') }} {{ resendCooldown() }}s</span>\n } @else {\n <a (click)=\"resendCode()\">{{ t('mfaResend') }}</a>\n }\n </p>\n\n <ion-button expand=\"block\" fill=\"clear\" color=\"dark\" shape=\"round\" (click)=\"backToStatus()\">\n {{ t('mfaCancel') }}\n </ion-button>\n } @case ('disable') { @if (hasPassword()) {\n <val-form [props]=\"disableFormProps()\" (onSubmit)=\"onDisableSubmit($event)\" />\n } @else if (mfaMethod() === 'TOTP') {\n <val-title [props]=\"{ content: t('mfaDisableTitle'), size: 'large', color: '', bold: false }\" />\n <val-text [props]=\"{ size: 'medium', color: 'dark', bold: false, content: t('mfaDisableTotpPrompt') }\" />\n <div class=\"mfa-pin\">\n <val-pin-input [props]=\"pinInputProps\" />\n </div>\n <ion-button expand=\"block\" shape=\"round\" [disabled]=\"working()\" (click)=\"disableWithMfaCode()\">\n @if (working()) {\n <ion-spinner name=\"crescent\"></ion-spinner>\n } @else { {{ t('mfaDisableButton') }} }\n </ion-button>\n } @else if (mfaMethod() === 'EMAIL' || mfaMethod() === 'SMS') {\n <val-title [props]=\"{ content: t('mfaDisableTitle'), size: 'large', color: '', bold: false }\" />\n @if (!disableCodeSent()) {\n <val-text [props]=\"{ size: 'medium', color: 'dark', bold: false, content: t('mfaDisableCodePrompt') }\" />\n <ion-button expand=\"block\" shape=\"round\" [disabled]=\"working()\" (click)=\"sendDisableCode()\">\n @if (working()) {\n <ion-spinner name=\"crescent\"></ion-spinner>\n } @else { {{ t('mfaDisableSendCode') }} }\n </ion-button>\n } @else {\n <val-text [props]=\"{ size: 'medium', color: 'dark', bold: false, content: t('mfaDisableCodePrompt') }\" />\n <div class=\"mfa-pin\">\n <val-pin-input [props]=\"pinInputProps\" />\n </div>\n <ion-button expand=\"block\" shape=\"round\" [disabled]=\"working()\" (click)=\"disableWithMfaCode()\">\n @if (working()) {\n <ion-spinner name=\"crescent\"></ion-spinner>\n } @else { {{ t('mfaDisableButton') }} }\n </ion-button>\n <ion-button expand=\"block\" fill=\"clear\" color=\"medium\" shape=\"round\" [disabled]=\"working()\" (click)=\"sendDisableCode()\">\n {{ t('mfaDisableResendCode') }}\n </ion-button>\n } } @else {\n <val-title [props]=\"{ content: t('mfaDisableTitle'), size: 'large', color: '', bold: false }\" />\n <val-text [props]=\"{ size: 'medium', color: 'dark', bold: false, content: t('mfaDisableNeedsPassword') }\" />\n }\n <ion-button expand=\"block\" fill=\"clear\" color=\"dark\" shape=\"round\" (click)=\"backToStatus()\">\n {{ t('mfaCancel') }}\n </ion-button>\n } }\n </section>\n </ion-content>\n </ng-template>\n</ion-modal>\n", styles: [".mfa-modal{display:flex;flex-direction:column;gap:14px;padding-top:4px}.mfa-loading{display:flex;justify-content:center;padding:40px 0}.mfa-status{display:inline-flex;align-items:center;width:fit-content;padding:4px 10px;border-radius:999px;font-size:.8125rem;font-weight:600;margin:0}.mfa-status--on{color:var(--ion-color-success-shade);background:rgba(var(--ion-color-success-rgb),.12)}.mfa-status--off{color:var(--ion-color-medium-shade);background:var(--ion-color-light)}.mfa-secret-row{display:flex;align-items:center;gap:8px}.mfa-secret-row .mfa-secret{flex:1;margin:0}.mfa-secret-row ion-button{flex-shrink:0;--color: var(--ion-color-dark);margin:0}.mfa-error{color:var(--ion-color-danger);font-size:.8125rem;margin:4px 0 0}.mfa-block,.mfa-backup{display:flex;flex-direction:column;gap:12px}.mfa-block{padding:16px;border-radius:14px;background:var(--ion-color-light)}.mfa-alert{display:flex;align-items:flex-start;gap:10px;padding:12px 14px;border-radius:10px;background:var(--ion-color-light);border-left:3px solid var(--ion-color-primary)}.mfa-alert ion-icon{font-size:1.25rem;color:var(--ion-color-primary);flex-shrink:0;margin-top:1px}.mfa-alert p{margin:0;font-size:.8125rem;line-height:1.45;color:var(--ion-color-dark)}.mfa-qr{display:flex;justify-content:center;padding:8px 0}.mfa-secret{display:block;padding:10px 12px;border-radius:8px;background:var(--ion-color-light);font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.9375rem;letter-spacing:.04em;word-break:break-all;text-align:center}.mfa-pin{display:flex;justify-content:center;padding:8px 0}.mfa-codes{display:grid;grid-template-columns:repeat(2,1fr);gap:8px;padding:12px;border-radius:12px;background:var(--ion-color-light)}.mfa-code{padding:8px;border-radius:6px;background:var(--ion-background-color, #fff);color:var(--ion-color-dark);font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.8125rem;text-align:center}.mfa-method-list{display:flex;flex-direction:column;gap:8px}.mfa-method-card{display:flex;align-items:flex-start;gap:12px;padding:14px 16px;border-radius:12px;border:1.5px solid var(--ion-border-color, rgba(var(--ion-color-dark-rgb), .14));background:transparent;cursor:pointer;text-align:left;width:100%;transition:border-color .15s ease,background .15s ease}.mfa-method-card--active{border-color:var(--ion-color-primary);background:rgba(var(--ion-color-primary-rgb),.07)}.mfa-method-card__dot{flex-shrink:0;width:18px;height:18px;border-radius:50%;border:2px solid var(--ion-color-medium);margin-top:2px;transition:border-color .15s,box-shadow .15s}.mfa-method-card--active .mfa-method-card__dot{border-color:var(--ion-color-primary);box-shadow:inset 0 0 0 4px var(--ion-color-primary)}.mfa-method-card__body{display:flex;flex-direction:column;gap:3px}.mfa-method-card__body strong{font-size:.9375rem;font-weight:600;color:var(--ion-color-dark)}.mfa-method-card__body span{font-size:.8125rem;color:var(--ion-color-medium);line-height:1.4}.mfa-resend{text-align:center;font-size:.875rem;color:var(--ion-color-dark);margin:4px 0}.mfa-resend a{color:var(--ion-color-primary);cursor:pointer;font-weight:600}\n"] }]
|
|
35818
35818
|
}], ctorParameters: () => [], propDecorators: { isOpen: [{
|
|
35819
35819
|
type: Input
|
|
35820
35820
|
}], prefillCode: [{
|
|
@@ -42280,7 +42280,7 @@ class AvatarUploadComponent {
|
|
|
42280
42280
|
<ion-header [translucent]="true">
|
|
42281
42281
|
<ion-toolbar>
|
|
42282
42282
|
<ion-buttons slot="end">
|
|
42283
|
-
<ion-button fill="clear" color="dark" (click)="showViewModal.set(false)">
|
|
42283
|
+
<ion-button fill="clear" color="dark" shape="round" (click)="showViewModal.set(false)">
|
|
42284
42284
|
<strong>{{ closePhotoLabel() }}</strong>
|
|
42285
42285
|
</ion-button>
|
|
42286
42286
|
</ion-buttons>
|
|
@@ -42387,7 +42387,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
42387
42387
|
<ion-header [translucent]="true">
|
|
42388
42388
|
<ion-toolbar>
|
|
42389
42389
|
<ion-buttons slot="end">
|
|
42390
|
-
<ion-button fill="clear" color="dark" (click)="showViewModal.set(false)">
|
|
42390
|
+
<ion-button fill="clear" color="dark" shape="round" (click)="showViewModal.set(false)">
|
|
42391
42391
|
<strong>{{ closePhotoLabel() }}</strong>
|
|
42392
42392
|
</ion-button>
|
|
42393
42393
|
</ion-buttons>
|
|
@@ -44805,6 +44805,7 @@ class LoginAttemptModalComponent {
|
|
|
44805
44805
|
fill: 'outline',
|
|
44806
44806
|
size: 'default',
|
|
44807
44807
|
type: 'button',
|
|
44808
|
+
shape: 'round',
|
|
44808
44809
|
state: ComponentStates.ENABLED,
|
|
44809
44810
|
};
|
|
44810
44811
|
}
|
|
@@ -44815,6 +44816,7 @@ class LoginAttemptModalComponent {
|
|
|
44815
44816
|
fill: 'solid',
|
|
44816
44817
|
size: 'default',
|
|
44817
44818
|
type: 'button',
|
|
44819
|
+
shape: 'round',
|
|
44818
44820
|
state: ComponentStates.ENABLED,
|
|
44819
44821
|
};
|
|
44820
44822
|
}
|
|
@@ -44825,6 +44827,7 @@ class LoginAttemptModalComponent {
|
|
|
44825
44827
|
fill: 'solid',
|
|
44826
44828
|
size: 'default',
|
|
44827
44829
|
type: 'button',
|
|
44830
|
+
shape: 'round',
|
|
44828
44831
|
state: ComponentStates.ENABLED,
|
|
44829
44832
|
};
|
|
44830
44833
|
}
|
|
@@ -44833,7 +44836,7 @@ class LoginAttemptModalComponent {
|
|
|
44833
44836
|
<ion-header>
|
|
44834
44837
|
<ion-toolbar>
|
|
44835
44838
|
<ion-buttons slot="end">
|
|
44836
|
-
<ion-button fill="clear" color="dark" (click)="close()">
|
|
44839
|
+
<ion-button fill="clear" color="dark" shape="round" (click)="close()">
|
|
44837
44840
|
<strong>{{ closeLabel }}</strong>
|
|
44838
44841
|
</ion-button>
|
|
44839
44842
|
</ion-buttons>
|
|
@@ -44924,7 +44927,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
44924
44927
|
<ion-header>
|
|
44925
44928
|
<ion-toolbar>
|
|
44926
44929
|
<ion-buttons slot="end">
|
|
44927
|
-
<ion-button fill="clear" color="dark" (click)="close()">
|
|
44930
|
+
<ion-button fill="clear" color="dark" shape="round" (click)="close()">
|
|
44928
44931
|
<strong>{{ closeLabel }}</strong>
|
|
44929
44932
|
</ion-button>
|
|
44930
44933
|
</ion-buttons>
|
|
@@ -45078,6 +45081,7 @@ class SessionListModalComponent {
|
|
|
45078
45081
|
fill: 'clear',
|
|
45079
45082
|
size: 'small',
|
|
45080
45083
|
type: 'button',
|
|
45084
|
+
shape: 'round',
|
|
45081
45085
|
state: ComponentStates.ENABLED,
|
|
45082
45086
|
};
|
|
45083
45087
|
}
|
|
@@ -45101,7 +45105,7 @@ class SessionListModalComponent {
|
|
|
45101
45105
|
<ion-header>
|
|
45102
45106
|
<ion-toolbar>
|
|
45103
45107
|
<ion-buttons slot="end">
|
|
45104
|
-
<ion-button fill="clear" color="dark" (click)="close()">
|
|
45108
|
+
<ion-button fill="clear" color="dark" shape="round" (click)="close()">
|
|
45105
45109
|
<strong>{{ closeLabel }}</strong>
|
|
45106
45110
|
</ion-button>
|
|
45107
45111
|
</ion-buttons>
|
|
@@ -45161,7 +45165,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
45161
45165
|
<ion-header>
|
|
45162
45166
|
<ion-toolbar>
|
|
45163
45167
|
<ion-buttons slot="end">
|
|
45164
|
-
<ion-button fill="clear" color="dark" (click)="close()">
|
|
45168
|
+
<ion-button fill="clear" color="dark" shape="round" (click)="close()">
|
|
45165
45169
|
<strong>{{ closeLabel }}</strong>
|
|
45166
45170
|
</ion-button>
|
|
45167
45171
|
</ion-buttons>
|
|
@@ -46822,7 +46826,7 @@ class CreateOrgModalComponent {
|
|
|
46822
46826
|
<ion-header>
|
|
46823
46827
|
<ion-toolbar>
|
|
46824
46828
|
<ion-buttons slot="end">
|
|
46825
|
-
<ion-button fill="clear" color="dark" (click)="dismiss()">
|
|
46829
|
+
<ion-button fill="clear" color="dark" shape="round" (click)="dismiss()">
|
|
46826
46830
|
<strong>{{ t('close') }}</strong>
|
|
46827
46831
|
</ion-button>
|
|
46828
46832
|
</ion-buttons>
|
|
@@ -46840,7 +46844,7 @@ class CreateOrgModalComponent {
|
|
|
46840
46844
|
/>
|
|
46841
46845
|
<val-form [props]="formMeta()" (onSubmit)="onSubmit($event)" />
|
|
46842
46846
|
<div class="modal-close-bottom">
|
|
46843
|
-
<ion-button expand="block" fill="outline" color="dark" (click)="dismiss()">
|
|
46847
|
+
<ion-button expand="block" fill="outline" color="dark" shape="round" (click)="dismiss()">
|
|
46844
46848
|
{{ t('close') }}
|
|
46845
46849
|
</ion-button>
|
|
46846
46850
|
</div>
|
|
@@ -46853,7 +46857,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
46853
46857
|
<ion-header>
|
|
46854
46858
|
<ion-toolbar>
|
|
46855
46859
|
<ion-buttons slot="end">
|
|
46856
|
-
<ion-button fill="clear" color="dark" (click)="dismiss()">
|
|
46860
|
+
<ion-button fill="clear" color="dark" shape="round" (click)="dismiss()">
|
|
46857
46861
|
<strong>{{ t('close') }}</strong>
|
|
46858
46862
|
</ion-button>
|
|
46859
46863
|
</ion-buttons>
|
|
@@ -46871,7 +46875,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
46871
46875
|
/>
|
|
46872
46876
|
<val-form [props]="formMeta()" (onSubmit)="onSubmit($event)" />
|
|
46873
46877
|
<div class="modal-close-bottom">
|
|
46874
|
-
<ion-button expand="block" fill="outline" color="dark" (click)="dismiss()">
|
|
46878
|
+
<ion-button expand="block" fill="outline" color="dark" shape="round" (click)="dismiss()">
|
|
46875
46879
|
{{ t('close') }}
|
|
46876
46880
|
</ion-button>
|
|
46877
46881
|
</div>
|
|
@@ -47035,6 +47039,7 @@ class DeleteAccountModalComponent {
|
|
|
47035
47039
|
fill: 'solid',
|
|
47036
47040
|
color: 'dark',
|
|
47037
47041
|
size: 'default',
|
|
47042
|
+
shape: 'round',
|
|
47038
47043
|
token,
|
|
47039
47044
|
state: ComponentStates.ENABLED,
|
|
47040
47045
|
};
|
|
@@ -47104,7 +47109,7 @@ class DeleteAccountModalComponent {
|
|
|
47104
47109
|
<ion-header>
|
|
47105
47110
|
<ion-toolbar>
|
|
47106
47111
|
<ion-buttons slot="end">
|
|
47107
|
-
<ion-button fill="clear" color="dark" (click)="close()">
|
|
47112
|
+
<ion-button fill="clear" color="dark" shape="round" (click)="close()">
|
|
47108
47113
|
<strong>{{ t('close') }}</strong>
|
|
47109
47114
|
</ion-button>
|
|
47110
47115
|
</ion-buttons>
|
|
@@ -47145,7 +47150,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
47145
47150
|
<ion-header>
|
|
47146
47151
|
<ion-toolbar>
|
|
47147
47152
|
<ion-buttons slot="end">
|
|
47148
|
-
<ion-button fill="clear" color="dark" (click)="close()">
|
|
47153
|
+
<ion-button fill="clear" color="dark" shape="round" (click)="close()">
|
|
47149
47154
|
<strong>{{ t('close') }}</strong>
|
|
47150
47155
|
</ion-button>
|
|
47151
47156
|
</ion-buttons>
|
|
@@ -47270,7 +47275,18 @@ class OrgInfoSheetComponent {
|
|
|
47270
47275
|
...this.closeProps,
|
|
47271
47276
|
text: this.closeProps.text || this.resolvedCloseLabel(),
|
|
47272
47277
|
}));
|
|
47273
|
-
|
|
47278
|
+
this.resolvedFeatures = computed(() => {
|
|
47279
|
+
if (this.features?.length)
|
|
47280
|
+
return this.features;
|
|
47281
|
+
if (!this.showFeatures)
|
|
47282
|
+
return [];
|
|
47283
|
+
return [
|
|
47284
|
+
{ icon: 'people-outline', title: this.tt('featureCollabTitle'), desc: this.tt('featureCollabDesc') },
|
|
47285
|
+
{ icon: 'swap-horizontal-outline', title: this.tt('featureSwitchTitle'), desc: this.tt('featureSwitchDesc') },
|
|
47286
|
+
{ icon: 'mail-outline', title: this.tt('featureInviteTitle'), desc: this.tt('featureInviteDesc') },
|
|
47287
|
+
];
|
|
47288
|
+
});
|
|
47289
|
+
addIcons({ peopleOutline, swapHorizontalOutline, mailOutline, codeSlashOutline, shieldCheckmarkOutline, warningOutline });
|
|
47274
47290
|
if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$f)) {
|
|
47275
47291
|
this.i18n.registerContent(DEFAULT_NAMESPACE$f, ORG_INFO_SHEET_I18N);
|
|
47276
47292
|
}
|
|
@@ -47282,11 +47298,11 @@ class OrgInfoSheetComponent {
|
|
|
47282
47298
|
return this.i18n.t(key, DEFAULT_NAMESPACE$f);
|
|
47283
47299
|
}
|
|
47284
47300
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: OrgInfoSheetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
47285
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: OrgInfoSheetComponent, isStandalone: true, selector: "val-org-info-sheet", inputs: { title: "title", subtitle: "subtitle", body: "body", closeLabel: "closeLabel", showFeatures: "showFeatures", closeProps: "closeProps", _modalRef: "_modalRef" }, ngImport: i0, template: `
|
|
47301
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: OrgInfoSheetComponent, isStandalone: true, selector: "val-org-info-sheet", inputs: { title: "title", subtitle: "subtitle", body: "body", closeLabel: "closeLabel", showFeatures: "showFeatures", features: "features", closeProps: "closeProps", _modalRef: "_modalRef" }, ngImport: i0, template: `
|
|
47286
47302
|
<ion-header>
|
|
47287
47303
|
<ion-toolbar>
|
|
47288
47304
|
<ion-buttons slot="end">
|
|
47289
|
-
<ion-button fill="clear" color="dark" (click)="dismiss()">
|
|
47305
|
+
<ion-button fill="clear" color="dark" shape="round" (click)="dismiss()">
|
|
47290
47306
|
<strong>{{ resolvedCloseLabel() }}</strong>
|
|
47291
47307
|
</ion-button>
|
|
47292
47308
|
</ion-buttons>
|
|
@@ -47297,29 +47313,17 @@ class OrgInfoSheetComponent {
|
|
|
47297
47313
|
<val-display [props]="{ content: resolvedTitle(), size: 'small', color: 'dark' }" />
|
|
47298
47314
|
<val-title [props]="{ content: resolvedSubtitle(), size: 'large', color: '', bold: false }" />
|
|
47299
47315
|
|
|
47300
|
-
@if (
|
|
47316
|
+
@if (resolvedFeatures().length > 0) {
|
|
47301
47317
|
<div class="info-features">
|
|
47302
|
-
|
|
47303
|
-
<
|
|
47304
|
-
|
|
47305
|
-
<div
|
|
47306
|
-
|
|
47307
|
-
|
|
47308
|
-
|
|
47309
|
-
<div class="info-feature">
|
|
47310
|
-
<ion-icon name="swap-horizontal-outline" class="info-feature__icon" />
|
|
47311
|
-
<div>
|
|
47312
|
-
<div class="info-feature__title">{{ tt('featureSwitchTitle') }}</div>
|
|
47313
|
-
<div class="info-feature__desc">{{ tt('featureSwitchDesc') }}</div>
|
|
47314
|
-
</div>
|
|
47315
|
-
</div>
|
|
47316
|
-
<div class="info-feature">
|
|
47317
|
-
<ion-icon name="mail-outline" class="info-feature__icon" />
|
|
47318
|
-
<div>
|
|
47319
|
-
<div class="info-feature__title">{{ tt('featureInviteTitle') }}</div>
|
|
47320
|
-
<div class="info-feature__desc">{{ tt('featureInviteDesc') }}</div>
|
|
47318
|
+
@for (f of resolvedFeatures(); track $index) {
|
|
47319
|
+
<div class="info-feature">
|
|
47320
|
+
<ion-icon [name]="f.icon" class="info-feature__icon" />
|
|
47321
|
+
<div>
|
|
47322
|
+
<div class="info-feature__title">{{ f.title }}</div>
|
|
47323
|
+
<div class="info-feature__desc">{{ f.desc }}</div>
|
|
47324
|
+
</div>
|
|
47321
47325
|
</div>
|
|
47322
|
-
|
|
47326
|
+
}
|
|
47323
47327
|
</div>
|
|
47324
47328
|
}
|
|
47325
47329
|
|
|
@@ -47357,7 +47361,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
47357
47361
|
<ion-header>
|
|
47358
47362
|
<ion-toolbar>
|
|
47359
47363
|
<ion-buttons slot="end">
|
|
47360
|
-
<ion-button fill="clear" color="dark" (click)="dismiss()">
|
|
47364
|
+
<ion-button fill="clear" color="dark" shape="round" (click)="dismiss()">
|
|
47361
47365
|
<strong>{{ resolvedCloseLabel() }}</strong>
|
|
47362
47366
|
</ion-button>
|
|
47363
47367
|
</ion-buttons>
|
|
@@ -47368,29 +47372,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
47368
47372
|
<val-display [props]="{ content: resolvedTitle(), size: 'small', color: 'dark' }" />
|
|
47369
47373
|
<val-title [props]="{ content: resolvedSubtitle(), size: 'large', color: '', bold: false }" />
|
|
47370
47374
|
|
|
47371
|
-
@if (
|
|
47375
|
+
@if (resolvedFeatures().length > 0) {
|
|
47372
47376
|
<div class="info-features">
|
|
47373
|
-
|
|
47374
|
-
<
|
|
47375
|
-
|
|
47376
|
-
<div
|
|
47377
|
-
|
|
47378
|
-
|
|
47379
|
-
|
|
47380
|
-
<div class="info-feature">
|
|
47381
|
-
<ion-icon name="swap-horizontal-outline" class="info-feature__icon" />
|
|
47382
|
-
<div>
|
|
47383
|
-
<div class="info-feature__title">{{ tt('featureSwitchTitle') }}</div>
|
|
47384
|
-
<div class="info-feature__desc">{{ tt('featureSwitchDesc') }}</div>
|
|
47385
|
-
</div>
|
|
47386
|
-
</div>
|
|
47387
|
-
<div class="info-feature">
|
|
47388
|
-
<ion-icon name="mail-outline" class="info-feature__icon" />
|
|
47389
|
-
<div>
|
|
47390
|
-
<div class="info-feature__title">{{ tt('featureInviteTitle') }}</div>
|
|
47391
|
-
<div class="info-feature__desc">{{ tt('featureInviteDesc') }}</div>
|
|
47377
|
+
@for (f of resolvedFeatures(); track $index) {
|
|
47378
|
+
<div class="info-feature">
|
|
47379
|
+
<ion-icon [name]="f.icon" class="info-feature__icon" />
|
|
47380
|
+
<div>
|
|
47381
|
+
<div class="info-feature__title">{{ f.title }}</div>
|
|
47382
|
+
<div class="info-feature__desc">{{ f.desc }}</div>
|
|
47383
|
+
</div>
|
|
47392
47384
|
</div>
|
|
47393
|
-
|
|
47385
|
+
}
|
|
47394
47386
|
</div>
|
|
47395
47387
|
}
|
|
47396
47388
|
|
|
@@ -47420,6 +47412,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
47420
47412
|
type: Input
|
|
47421
47413
|
}], showFeatures: [{
|
|
47422
47414
|
type: Input
|
|
47415
|
+
}], features: [{
|
|
47416
|
+
type: Input
|
|
47423
47417
|
}], closeProps: [{
|
|
47424
47418
|
type: Input
|
|
47425
47419
|
}], _modalRef: [{
|
|
@@ -47545,7 +47539,7 @@ class SwitchOrgModalComponent {
|
|
|
47545
47539
|
<ion-header>
|
|
47546
47540
|
<ion-toolbar>
|
|
47547
47541
|
<ion-buttons slot="end">
|
|
47548
|
-
<ion-button fill="clear" color="dark" (click)="dismiss()">
|
|
47542
|
+
<ion-button fill="clear" color="dark" shape="round" (click)="dismiss()">
|
|
47549
47543
|
<strong>{{ t('close') }}</strong>
|
|
47550
47544
|
</ion-button>
|
|
47551
47545
|
</ion-buttons>
|
|
@@ -47636,7 +47630,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
47636
47630
|
<ion-header>
|
|
47637
47631
|
<ion-toolbar>
|
|
47638
47632
|
<ion-buttons slot="end">
|
|
47639
|
-
<ion-button fill="clear" color="dark" (click)="dismiss()">
|
|
47633
|
+
<ion-button fill="clear" color="dark" shape="round" (click)="dismiss()">
|
|
47640
47634
|
<strong>{{ t('close') }}</strong>
|
|
47641
47635
|
</ion-button>
|
|
47642
47636
|
</ion-buttons>
|
|
@@ -48803,7 +48797,7 @@ class EditOrgModalComponent {
|
|
|
48803
48797
|
<ion-header>
|
|
48804
48798
|
<ion-toolbar>
|
|
48805
48799
|
<ion-buttons slot="end">
|
|
48806
|
-
<ion-button fill="clear" color="dark" (click)="dismiss()">
|
|
48800
|
+
<ion-button fill="clear" color="dark" shape="round" (click)="dismiss()">
|
|
48807
48801
|
<strong>{{ t('close') }}</strong>
|
|
48808
48802
|
</ion-button>
|
|
48809
48803
|
</ion-buttons>
|
|
@@ -48821,7 +48815,7 @@ class EditOrgModalComponent {
|
|
|
48821
48815
|
/>
|
|
48822
48816
|
<val-form [props]="formMeta()" (onSubmit)="onFormSubmit($event)" />
|
|
48823
48817
|
<div class="modal-close-bottom">
|
|
48824
|
-
<ion-button expand="block" fill="outline" color="dark" (click)="dismiss()">
|
|
48818
|
+
<ion-button expand="block" fill="outline" color="dark" shape="round" (click)="dismiss()">
|
|
48825
48819
|
{{ t('close') }}
|
|
48826
48820
|
</ion-button>
|
|
48827
48821
|
</div>
|
|
@@ -48834,7 +48828,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
48834
48828
|
<ion-header>
|
|
48835
48829
|
<ion-toolbar>
|
|
48836
48830
|
<ion-buttons slot="end">
|
|
48837
|
-
<ion-button fill="clear" color="dark" (click)="dismiss()">
|
|
48831
|
+
<ion-button fill="clear" color="dark" shape="round" (click)="dismiss()">
|
|
48838
48832
|
<strong>{{ t('close') }}</strong>
|
|
48839
48833
|
</ion-button>
|
|
48840
48834
|
</ion-buttons>
|
|
@@ -48852,7 +48846,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
48852
48846
|
/>
|
|
48853
48847
|
<val-form [props]="formMeta()" (onSubmit)="onFormSubmit($event)" />
|
|
48854
48848
|
<div class="modal-close-bottom">
|
|
48855
|
-
<ion-button expand="block" fill="outline" color="dark" (click)="dismiss()">
|
|
48849
|
+
<ion-button expand="block" fill="outline" color="dark" shape="round" (click)="dismiss()">
|
|
48856
48850
|
{{ t('close') }}
|
|
48857
48851
|
</ion-button>
|
|
48858
48852
|
</div>
|
|
@@ -48982,6 +48976,7 @@ class InviteMemberModalComponent {
|
|
|
48982
48976
|
fill: 'solid',
|
|
48983
48977
|
expand: 'block',
|
|
48984
48978
|
size: 'default',
|
|
48979
|
+
shape: 'round',
|
|
48985
48980
|
type: 'button',
|
|
48986
48981
|
state: this.sending() ? ComponentStates.WORKING : ComponentStates.ENABLED,
|
|
48987
48982
|
}));
|
|
@@ -48992,6 +48987,7 @@ class InviteMemberModalComponent {
|
|
|
48992
48987
|
fill: 'solid',
|
|
48993
48988
|
expand: 'block',
|
|
48994
48989
|
size: 'default',
|
|
48990
|
+
shape: 'round',
|
|
48995
48991
|
type: 'button',
|
|
48996
48992
|
state: this.sending() ? ComponentStates.WORKING : ComponentStates.ENABLED,
|
|
48997
48993
|
}));
|
|
@@ -49164,7 +49160,7 @@ class InviteMemberModalComponent {
|
|
|
49164
49160
|
<ion-header>
|
|
49165
49161
|
<ion-toolbar>
|
|
49166
49162
|
<ion-buttons slot="end">
|
|
49167
|
-
<ion-button fill="clear" color="dark" (click)="dismiss()">
|
|
49163
|
+
<ion-button fill="clear" color="dark" shape="round" (click)="dismiss()">
|
|
49168
49164
|
<strong>{{ t('close') }}</strong>
|
|
49169
49165
|
</ion-button>
|
|
49170
49166
|
</ion-buttons>
|
|
@@ -49276,7 +49272,7 @@ class InviteMemberModalComponent {
|
|
|
49276
49272
|
</div>
|
|
49277
49273
|
|
|
49278
49274
|
<div class="modal-close-bottom">
|
|
49279
|
-
<ion-button expand="block" fill="outline" color="dark" (click)="dismiss()">
|
|
49275
|
+
<ion-button expand="block" fill="outline" color="dark" shape="round" (click)="dismiss()">
|
|
49280
49276
|
{{ t('close') }}
|
|
49281
49277
|
</ion-button>
|
|
49282
49278
|
</div>
|
|
@@ -49305,7 +49301,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
49305
49301
|
<ion-header>
|
|
49306
49302
|
<ion-toolbar>
|
|
49307
49303
|
<ion-buttons slot="end">
|
|
49308
|
-
<ion-button fill="clear" color="dark" (click)="dismiss()">
|
|
49304
|
+
<ion-button fill="clear" color="dark" shape="round" (click)="dismiss()">
|
|
49309
49305
|
<strong>{{ t('close') }}</strong>
|
|
49310
49306
|
</ion-button>
|
|
49311
49307
|
</ion-buttons>
|
|
@@ -49417,7 +49413,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
49417
49413
|
</div>
|
|
49418
49414
|
|
|
49419
49415
|
<div class="modal-close-bottom">
|
|
49420
|
-
<ion-button expand="block" fill="outline" color="dark" (click)="dismiss()">
|
|
49416
|
+
<ion-button expand="block" fill="outline" color="dark" shape="round" (click)="dismiss()">
|
|
49421
49417
|
{{ t('close') }}
|
|
49422
49418
|
</ion-button>
|
|
49423
49419
|
</div>
|
|
@@ -49697,6 +49693,7 @@ class MemberDetailModalComponent {
|
|
|
49697
49693
|
fill: 'outline',
|
|
49698
49694
|
expand: 'block',
|
|
49699
49695
|
size: 'default',
|
|
49696
|
+
shape: 'round',
|
|
49700
49697
|
type: 'button',
|
|
49701
49698
|
state: this.working() ? ComponentStates.WORKING : ComponentStates.ENABLED,
|
|
49702
49699
|
}));
|
|
@@ -49814,7 +49811,7 @@ class MemberDetailModalComponent {
|
|
|
49814
49811
|
<ion-header>
|
|
49815
49812
|
<ion-toolbar>
|
|
49816
49813
|
<ion-buttons slot="end">
|
|
49817
|
-
<ion-button fill="clear" color="dark" (click)="dismiss()">
|
|
49814
|
+
<ion-button fill="clear" color="dark" shape="round" (click)="dismiss()">
|
|
49818
49815
|
<strong>{{ t('close') }}</strong>
|
|
49819
49816
|
</ion-button>
|
|
49820
49817
|
</ion-buttons>
|
|
@@ -49885,7 +49882,7 @@ class MemberDetailModalComponent {
|
|
|
49885
49882
|
}
|
|
49886
49883
|
|
|
49887
49884
|
<div class="modal-close-bottom">
|
|
49888
|
-
<ion-button expand="block" fill="outline" color="dark" (click)="dismiss()">
|
|
49885
|
+
<ion-button expand="block" fill="outline" color="dark" shape="round" (click)="dismiss()">
|
|
49889
49886
|
{{ t('close') }}
|
|
49890
49887
|
</ion-button>
|
|
49891
49888
|
</div>
|
|
@@ -49911,7 +49908,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
49911
49908
|
<ion-header>
|
|
49912
49909
|
<ion-toolbar>
|
|
49913
49910
|
<ion-buttons slot="end">
|
|
49914
|
-
<ion-button fill="clear" color="dark" (click)="dismiss()">
|
|
49911
|
+
<ion-button fill="clear" color="dark" shape="round" (click)="dismiss()">
|
|
49915
49912
|
<strong>{{ t('close') }}</strong>
|
|
49916
49913
|
</ion-button>
|
|
49917
49914
|
</ion-buttons>
|
|
@@ -49982,7 +49979,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
49982
49979
|
}
|
|
49983
49980
|
|
|
49984
49981
|
<div class="modal-close-bottom">
|
|
49985
|
-
<ion-button expand="block" fill="outline" color="dark" (click)="dismiss()">
|
|
49982
|
+
<ion-button expand="block" fill="outline" color="dark" shape="round" (click)="dismiss()">
|
|
49986
49983
|
{{ t('close') }}
|
|
49987
49984
|
</ion-button>
|
|
49988
49985
|
</div>
|
|
@@ -50094,7 +50091,7 @@ class TransferOwnershipModalComponent {
|
|
|
50094
50091
|
<ion-header>
|
|
50095
50092
|
<ion-toolbar>
|
|
50096
50093
|
<ion-buttons slot="end">
|
|
50097
|
-
<ion-button fill="clear" color="dark" (click)="dismiss()">
|
|
50094
|
+
<ion-button fill="clear" color="dark" shape="round" (click)="dismiss()">
|
|
50098
50095
|
<strong>{{ t('close') }}</strong>
|
|
50099
50096
|
</ion-button>
|
|
50100
50097
|
</ion-buttons>
|
|
@@ -50154,13 +50151,13 @@ class TransferOwnershipModalComponent {
|
|
|
50154
50151
|
}
|
|
50155
50152
|
|
|
50156
50153
|
<div class="transfer-confirm-row">
|
|
50157
|
-
<ion-button expand="block" color="dark" [disabled]="!selectedId()" (click)="confirm()">
|
|
50154
|
+
<ion-button expand="block" color="dark" shape="round" [disabled]="!selectedId()" (click)="confirm()">
|
|
50158
50155
|
{{ t('confirmCta') }}
|
|
50159
50156
|
</ion-button>
|
|
50160
50157
|
</div>
|
|
50161
50158
|
|
|
50162
50159
|
<div class="modal-close-bottom">
|
|
50163
|
-
<ion-button expand="block" fill="outline" color="dark" (click)="dismiss()">
|
|
50160
|
+
<ion-button expand="block" fill="outline" color="dark" shape="round" (click)="dismiss()">
|
|
50164
50161
|
{{ t('close') }}
|
|
50165
50162
|
</ion-button>
|
|
50166
50163
|
</div>
|
|
@@ -50183,7 +50180,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
50183
50180
|
<ion-header>
|
|
50184
50181
|
<ion-toolbar>
|
|
50185
50182
|
<ion-buttons slot="end">
|
|
50186
|
-
<ion-button fill="clear" color="dark" (click)="dismiss()">
|
|
50183
|
+
<ion-button fill="clear" color="dark" shape="round" (click)="dismiss()">
|
|
50187
50184
|
<strong>{{ t('close') }}</strong>
|
|
50188
50185
|
</ion-button>
|
|
50189
50186
|
</ion-buttons>
|
|
@@ -50243,13 +50240,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
50243
50240
|
}
|
|
50244
50241
|
|
|
50245
50242
|
<div class="transfer-confirm-row">
|
|
50246
|
-
<ion-button expand="block" color="dark" [disabled]="!selectedId()" (click)="confirm()">
|
|
50243
|
+
<ion-button expand="block" color="dark" shape="round" [disabled]="!selectedId()" (click)="confirm()">
|
|
50247
50244
|
{{ t('confirmCta') }}
|
|
50248
50245
|
</ion-button>
|
|
50249
50246
|
</div>
|
|
50250
50247
|
|
|
50251
50248
|
<div class="modal-close-bottom">
|
|
50252
|
-
<ion-button expand="block" fill="outline" color="dark" (click)="dismiss()">
|
|
50249
|
+
<ion-button expand="block" fill="outline" color="dark" shape="round" (click)="dismiss()">
|
|
50253
50250
|
{{ t('close') }}
|
|
50254
50251
|
</ion-button>
|
|
50255
50252
|
</div>
|
|
@@ -50506,7 +50503,7 @@ class MemberImportModalComponent {
|
|
|
50506
50503
|
<ion-header>
|
|
50507
50504
|
<ion-toolbar>
|
|
50508
50505
|
<ion-buttons slot="end">
|
|
50509
|
-
<ion-button fill="clear" color="dark" (click)="dismiss()">
|
|
50506
|
+
<ion-button fill="clear" color="dark" shape="round" (click)="dismiss()">
|
|
50510
50507
|
<strong>{{ t('close') }}</strong>
|
|
50511
50508
|
</ion-button>
|
|
50512
50509
|
</ion-buttons>
|
|
@@ -50604,7 +50601,7 @@ class MemberImportModalComponent {
|
|
|
50604
50601
|
}
|
|
50605
50602
|
|
|
50606
50603
|
<div class="import-actions">
|
|
50607
|
-
<ion-button expand="block" color="dark" [disabled]="submitting()" (click)="submit()">
|
|
50604
|
+
<ion-button expand="block" color="dark" shape="round" [disabled]="submitting()" (click)="submit()">
|
|
50608
50605
|
@if (submitting()) {
|
|
50609
50606
|
<ion-spinner name="dots" />
|
|
50610
50607
|
{{ t('submitting') }}
|
|
@@ -50628,14 +50625,14 @@ class MemberImportModalComponent {
|
|
|
50628
50625
|
}
|
|
50629
50626
|
</div>
|
|
50630
50627
|
<div class="import-actions">
|
|
50631
|
-
<ion-button expand="block" fill="outline" color="dark" (click)="reset()">
|
|
50628
|
+
<ion-button expand="block" fill="outline" color="dark" shape="round" (click)="reset()">
|
|
50632
50629
|
{{ t('again') }}
|
|
50633
50630
|
</ion-button>
|
|
50634
50631
|
</div>
|
|
50635
50632
|
}
|
|
50636
50633
|
|
|
50637
50634
|
<div class="modal-close-bottom">
|
|
50638
|
-
<ion-button expand="block" fill="outline" color="dark" (click)="dismiss()">
|
|
50635
|
+
<ion-button expand="block" fill="outline" color="dark" shape="round" (click)="dismiss()">
|
|
50639
50636
|
{{ t('close') }}
|
|
50640
50637
|
</ion-button>
|
|
50641
50638
|
</div>
|
|
@@ -50664,7 +50661,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
50664
50661
|
<ion-header>
|
|
50665
50662
|
<ion-toolbar>
|
|
50666
50663
|
<ion-buttons slot="end">
|
|
50667
|
-
<ion-button fill="clear" color="dark" (click)="dismiss()">
|
|
50664
|
+
<ion-button fill="clear" color="dark" shape="round" (click)="dismiss()">
|
|
50668
50665
|
<strong>{{ t('close') }}</strong>
|
|
50669
50666
|
</ion-button>
|
|
50670
50667
|
</ion-buttons>
|
|
@@ -50762,7 +50759,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
50762
50759
|
}
|
|
50763
50760
|
|
|
50764
50761
|
<div class="import-actions">
|
|
50765
|
-
<ion-button expand="block" color="dark" [disabled]="submitting()" (click)="submit()">
|
|
50762
|
+
<ion-button expand="block" color="dark" shape="round" [disabled]="submitting()" (click)="submit()">
|
|
50766
50763
|
@if (submitting()) {
|
|
50767
50764
|
<ion-spinner name="dots" />
|
|
50768
50765
|
{{ t('submitting') }}
|
|
@@ -50786,14 +50783,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
50786
50783
|
}
|
|
50787
50784
|
</div>
|
|
50788
50785
|
<div class="import-actions">
|
|
50789
|
-
<ion-button expand="block" fill="outline" color="dark" (click)="reset()">
|
|
50786
|
+
<ion-button expand="block" fill="outline" color="dark" shape="round" (click)="reset()">
|
|
50790
50787
|
{{ t('again') }}
|
|
50791
50788
|
</ion-button>
|
|
50792
50789
|
</div>
|
|
50793
50790
|
}
|
|
50794
50791
|
|
|
50795
50792
|
<div class="modal-close-bottom">
|
|
50796
|
-
<ion-button expand="block" fill="outline" color="dark" (click)="dismiss()">
|
|
50793
|
+
<ion-button expand="block" fill="outline" color="dark" shape="round" (click)="dismiss()">
|
|
50797
50794
|
{{ t('close') }}
|
|
50798
50795
|
</ion-button>
|
|
50799
50796
|
</div>
|
|
@@ -50873,8 +50870,14 @@ const ORGANIZATION_VIEW_I18N = {
|
|
|
50873
50870
|
apiKeysMoreInfo: 'Más información',
|
|
50874
50871
|
apiKeysInfoTitle: 'Integraciones por API',
|
|
50875
50872
|
apiKeysInfoSubtitle: '¿Qué puedes hacer con una API Key?',
|
|
50876
|
-
apiKeysInfoContent: '
|
|
50873
|
+
apiKeysInfoContent: 'Revoca cualquier key en cualquier momento desde la sección Integraciones.',
|
|
50877
50874
|
apiKeysInfoClose: 'Cerrar',
|
|
50875
|
+
apiKeysFeatureAutoTitle: 'Automatización',
|
|
50876
|
+
apiKeysFeatureAutoDesc: 'Conecta tus sistemas externos directamente, sin abrir la app.',
|
|
50877
|
+
apiKeysFeaturePermsTitle: 'Permisos a medida',
|
|
50878
|
+
apiKeysFeaturePermsDesc: 'Cada key lleva solo los permisos que tú le asignes — concede el mínimo.',
|
|
50879
|
+
apiKeysFeatureSecretTitle: 'Secreto único',
|
|
50880
|
+
apiKeysFeatureSecretDesc: 'El secreto se muestra una sola vez. Guárdalo en un gestor de secretos.',
|
|
50878
50881
|
orgsMoreInfo: 'Aprender más sobre organizaciones',
|
|
50879
50882
|
orgsInfoTitle: '¿Qué son las organizaciones?',
|
|
50880
50883
|
orgsInfoSubtitle: 'Todo lo que necesitas saber',
|
|
@@ -50956,8 +50959,14 @@ const ORGANIZATION_VIEW_I18N = {
|
|
|
50956
50959
|
apiKeysMoreInfo: 'More information',
|
|
50957
50960
|
apiKeysInfoTitle: 'API integrations',
|
|
50958
50961
|
apiKeysInfoSubtitle: 'What can you do with an API Key?',
|
|
50959
|
-
apiKeysInfoContent: '
|
|
50962
|
+
apiKeysInfoContent: 'Revoke any key at any time from the Integrations section.',
|
|
50960
50963
|
apiKeysInfoClose: 'Close',
|
|
50964
|
+
apiKeysFeatureAutoTitle: 'Automation',
|
|
50965
|
+
apiKeysFeatureAutoDesc: 'Connect your external systems directly, without opening the app.',
|
|
50966
|
+
apiKeysFeaturePermsTitle: 'Custom permissions',
|
|
50967
|
+
apiKeysFeaturePermsDesc: 'Each key carries only the permissions you assign — grant the minimum.',
|
|
50968
|
+
apiKeysFeatureSecretTitle: 'One-time secret',
|
|
50969
|
+
apiKeysFeatureSecretDesc: 'The secret is shown only once. Store it in a secret manager.',
|
|
50961
50970
|
orgsMoreInfo: 'Learn more about organizations',
|
|
50962
50971
|
orgsInfoTitle: 'What are organizations?',
|
|
50963
50972
|
orgsInfoSubtitle: 'Everything you need to know',
|
|
@@ -51187,7 +51196,7 @@ class OrganizationViewComponent {
|
|
|
51187
51196
|
this.openApiKeysButtonProps = computed(() => ({
|
|
51188
51197
|
text: this.tt('apiKeysOpen'),
|
|
51189
51198
|
color: 'dark',
|
|
51190
|
-
fill: '
|
|
51199
|
+
fill: 'outline',
|
|
51191
51200
|
size: 'default',
|
|
51192
51201
|
shape: 'round',
|
|
51193
51202
|
type: 'button',
|
|
@@ -51332,6 +51341,11 @@ class OrganizationViewComponent {
|
|
|
51332
51341
|
component: OrgInfoSheetComponent,
|
|
51333
51342
|
componentProps: {
|
|
51334
51343
|
showFeatures: false,
|
|
51344
|
+
features: [
|
|
51345
|
+
{ icon: 'code-slash-outline', title: this.tt('apiKeysFeatureAutoTitle'), desc: this.tt('apiKeysFeatureAutoDesc') },
|
|
51346
|
+
{ icon: 'shield-checkmark-outline', title: this.tt('apiKeysFeaturePermsTitle'), desc: this.tt('apiKeysFeaturePermsDesc') },
|
|
51347
|
+
{ icon: 'warning-outline', title: this.tt('apiKeysFeatureSecretTitle'), desc: this.tt('apiKeysFeatureSecretDesc') },
|
|
51348
|
+
],
|
|
51335
51349
|
title: this.tt('apiKeysInfoTitle'),
|
|
51336
51350
|
subtitle: this.tt('apiKeysInfoSubtitle'),
|
|
51337
51351
|
body: this.tt('apiKeysInfoContent'),
|
|
@@ -51343,6 +51357,7 @@ class OrganizationViewComponent {
|
|
|
51343
51357
|
size: 'default',
|
|
51344
51358
|
expand: 'block',
|
|
51345
51359
|
type: 'button',
|
|
51360
|
+
shape: 'round',
|
|
51346
51361
|
},
|
|
51347
51362
|
},
|
|
51348
51363
|
breakpoints: { breakpoints: [0, 0.92], initialBreakpoint: 0.92, showHandle: true },
|
|
@@ -53824,7 +53839,7 @@ class ApiKeyCreateModalComponent {
|
|
|
53824
53839
|
<ion-header>
|
|
53825
53840
|
<ion-toolbar>
|
|
53826
53841
|
<ion-buttons slot="end">
|
|
53827
|
-
<ion-button fill="clear" color="dark" (click)="dismiss()">
|
|
53842
|
+
<ion-button fill="clear" color="dark" shape="round" (click)="dismiss()">
|
|
53828
53843
|
<strong>{{ t('close') }}</strong>
|
|
53829
53844
|
</ion-button>
|
|
53830
53845
|
</ion-buttons>
|
|
@@ -53839,10 +53854,10 @@ class ApiKeyCreateModalComponent {
|
|
|
53839
53854
|
<code>{{ s.secret }}</code>
|
|
53840
53855
|
</div>
|
|
53841
53856
|
<div class="apikey-actions">
|
|
53842
|
-
<ion-button expand="block" color="dark" (click)="copy(s.secret)">
|
|
53857
|
+
<ion-button expand="block" color="dark" shape="round" (click)="copy(s.secret)">
|
|
53843
53858
|
{{ copied() ? t('copied') : t('copy') }}
|
|
53844
53859
|
</ion-button>
|
|
53845
|
-
<ion-button expand="block" fill="outline" color="dark" (click)="finishSecret()">
|
|
53860
|
+
<ion-button expand="block" fill="outline" color="dark" shape="round" (click)="finishSecret()">
|
|
53846
53861
|
{{ t('done') }}
|
|
53847
53862
|
</ion-button>
|
|
53848
53863
|
</div>
|
|
@@ -53882,7 +53897,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
53882
53897
|
<ion-header>
|
|
53883
53898
|
<ion-toolbar>
|
|
53884
53899
|
<ion-buttons slot="end">
|
|
53885
|
-
<ion-button fill="clear" color="dark" (click)="dismiss()">
|
|
53900
|
+
<ion-button fill="clear" color="dark" shape="round" (click)="dismiss()">
|
|
53886
53901
|
<strong>{{ t('close') }}</strong>
|
|
53887
53902
|
</ion-button>
|
|
53888
53903
|
</ion-buttons>
|
|
@@ -53897,10 +53912,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
53897
53912
|
<code>{{ s.secret }}</code>
|
|
53898
53913
|
</div>
|
|
53899
53914
|
<div class="apikey-actions">
|
|
53900
|
-
<ion-button expand="block" color="dark" (click)="copy(s.secret)">
|
|
53915
|
+
<ion-button expand="block" color="dark" shape="round" (click)="copy(s.secret)">
|
|
53901
53916
|
{{ copied() ? t('copied') : t('copy') }}
|
|
53902
53917
|
</ion-button>
|
|
53903
|
-
<ion-button expand="block" fill="outline" color="dark" (click)="finishSecret()">
|
|
53918
|
+
<ion-button expand="block" fill="outline" color="dark" shape="round" (click)="finishSecret()">
|
|
53904
53919
|
{{ t('done') }}
|
|
53905
53920
|
</ion-button>
|
|
53906
53921
|
</div>
|
|
@@ -54458,7 +54473,7 @@ function createRefreshableStream(factory, options) {
|
|
|
54458
54473
|
// sesión NUNCA se establece (token inválido, signInWithFirebase falló), el
|
|
54459
54474
|
// gate igual se libera tras `authGateTimeoutMs` y la factory procede: si no
|
|
54460
54475
|
// hay permiso terminará en estado `error`, jamás en skeleton infinito.
|
|
54461
|
-
// Tag los dos inputs del `race` para
|
|
54476
|
+
// Tag los dos inputs del `race` para distivdsr en logs cuál ganó: la sesión
|
|
54462
54477
|
// de Firebase Auth (`auth-ready`) o el timeout de seguridad (`timeout`).
|
|
54463
54478
|
const authGate$ = firebase
|
|
54464
54479
|
? race(firebase.firebaseAuthReady$.pipe(map$1(() => 'auth-ready')), timer(authGateTimeoutMs).pipe(map$1(() => 'timeout'))).pipe(take(1), tap(winner => {
|
|
@@ -55427,7 +55442,6 @@ class ContentReactionComponent {
|
|
|
55427
55442
|
return this.resolvedProps().showComment;
|
|
55428
55443
|
});
|
|
55429
55444
|
this.canSubmit = computed(() => this.state().selectedValue !== null && !this.state().isLoading);
|
|
55430
|
-
addIcons({ closeOutline });
|
|
55431
55445
|
// Auto-registro de defaults i18n (es/en) si el consumer no proveyó el
|
|
55432
55446
|
// namespace. Garantiza que `t()` nunca caiga en key faltante. Ver
|
|
55433
55447
|
// CONTENT_REACTION_I18N. Idempotente: solo la 1ª instancia registra.
|
|
@@ -55578,11 +55592,11 @@ class ContentReactionComponent {
|
|
|
55578
55592
|
return this.i18n.t(key, 'ContentReaction') || translations[key] || key;
|
|
55579
55593
|
}
|
|
55580
55594
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ContentReactionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
55581
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: ContentReactionComponent, isStandalone: true, selector: "val-content-reaction", inputs: { props: "props", _modalRef: "_modalRef" }, outputs: { reactionSubmit: "reactionSubmit", reactionChange: "reactionChange" }, usesOnChanges: true, ngImport: i0, template: "@if (isConfigured()) {\n<div class=\"content-reaction\" [class.disabled]=\"resolvedProps().disabled\" [class.readonly]=\"resolvedProps().readonly\">\n <!-- Bot\u00F3n de cierre (solo en modo modal/sheet) -->\n @if (resolvedProps().showCloseButton) {\n <ion-button fill=\"clear\" color=\"dark\"
|
|
55595
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: ContentReactionComponent, isStandalone: true, selector: "val-content-reaction", inputs: { props: "props", _modalRef: "_modalRef" }, outputs: { reactionSubmit: "reactionSubmit", reactionChange: "reactionChange" }, usesOnChanges: true, ngImport: i0, template: "@if (isConfigured()) {\n<div class=\"content-reaction\" [class.disabled]=\"resolvedProps().disabled\" [class.readonly]=\"resolvedProps().readonly\">\n <!-- Bot\u00F3n de cierre (solo en modo modal/sheet) -->\n @if (resolvedProps().showCloseButton) {\n <ion-button fill=\"clear\" color=\"dark\" shape=\"round\" class=\"close-button\" (click)=\"close()\">\n <strong>{{ t('close') }}</strong>\n </ion-button>\n }\n <!-- Loading inicial -->\n @if (state().isLoading && !state().selectedValue) {\n <div class=\"loading-container\">\n <ion-spinner name=\"crescent\"></ion-spinner>\n </div>\n } @else {\n <!-- Pregunta -->\n <p class=\"question\">\n {{ state().selectedValue === 'negative' ? t('negativeFeedbackTitle') : resolvedProps().question }}\n </p>\n\n <!-- Botones de reacci\u00F3n \u2014 variante emoji (caras, estrellas, thumbs, \u2026) -->\n @if (resolvedProps().variant === 'emoji') {\n <div class=\"reaction-buttons reaction-buttons--emoji\">\n @for (value of activeReactionValues(); track value; let i = $index) {\n <ion-button\n fill=\"clear\"\n shape=\"round\"\n [class.selected]=\"isSelected(value)\"\n [attr.aria-label]=\"resolvedProps().emojiLabels[i]\"\n [attr.aria-pressed]=\"isSelected(value)\"\n [disabled]=\"resolvedProps().disabled || resolvedProps().readonly\"\n (click)=\"selectReaction(value)\"\n >\n <span class=\"emoji-btn\">{{ resolvedProps().emojis[i] }}</span>\n </ion-button>\n }\n </div>\n } @else {\n <!-- Botones de reacci\u00F3n \u2014 variante texto (default) -->\n <div class=\"reaction-buttons\">\n <ion-button\n [fill]=\"isSelected('positive') ? 'solid' : 'outline'\"\n shape=\"round\"\n color=\"dark\"\n [attr.aria-pressed]=\"isSelected('positive')\"\n [disabled]=\"resolvedProps().disabled || resolvedProps().readonly\"\n (click)=\"selectReaction('positive')\"\n >\n {{ t('positiveLabel') }}\n </ion-button>\n <ion-button\n [fill]=\"isSelected('negative') ? 'solid' : 'outline'\"\n shape=\"round\"\n color=\"dark\"\n [attr.aria-pressed]=\"isSelected('negative')\"\n [disabled]=\"resolvedProps().disabled || resolvedProps().readonly\"\n (click)=\"selectReaction('negative')\"\n >\n {{ t('negativeLabel') }}\n </ion-button>\n </div>\n }\n\n <!-- Campo de comentario (solo si hay selecci\u00F3n) -->\n @if (showCommentField()) {\n <div class=\"comment-section\">\n <ion-textarea\n [value]=\"state().comment\"\n [placeholder]=\"resolvedProps().commentPlaceholder\"\n [maxlength]=\"resolvedProps().maxCommentLength\"\n [disabled]=\"resolvedProps().disabled\"\n [rows]=\"3\"\n class=\"comment-textarea\"\n (ionInput)=\"updateComment($event)\"\n ></ion-textarea>\n <span class=\"char-count\"> {{ state().comment.length }}/{{ resolvedProps().maxCommentLength }} </span>\n </div>\n }\n\n <!-- Bot\u00F3n de env\u00EDo -->\n @if (state().selectedValue && !state().isSubmitted) {\n <ion-button expand=\"block\" shape=\"round\" [disabled]=\"!canSubmit()\" (click)=\"submitReaction()\" class=\"submit-button\">\n @if (state().isLoading) {\n <ion-spinner name=\"crescent\"></ion-spinner>\n } @else { {{ state().hadPreviousReaction ? t('update') : t('submit') }} }\n </ion-button>\n }\n\n <!-- Mensaje de confirmaci\u00F3n -->\n @if (state().isSubmitted) {\n <p class=\"submitted-message\">{{ t('thankYou') }}</p>\n }\n\n <!-- Error -->\n @if (state().error) {\n <p class=\"error-message\">{{ state().error }}</p>\n } }\n</div>\n}\n", styles: [":host{display:block}.content-reaction{position:relative;padding:16px;text-align:center}.content-reaction.disabled{opacity:.6;pointer-events:none}.content-reaction.readonly{pointer-events:none}.close-button{position:absolute;top:0;right:0;margin:0;--padding-start: 6px;--padding-end: 6px;--color: var(--ion-color-medium)}.close-button ion-icon{font-size:1.5rem}.question{font-size:1.0625rem;font-weight:700;color:var(--ion-color-dark);margin:0 0 16px}.reaction-buttons{display:flex;justify-content:center;gap:12px;margin-bottom:20px}.reaction-buttons--emoji{gap:8px}.reaction-buttons--emoji ion-button{--padding-start: 4px;--padding-end: 4px;transition:transform .15s ease}.reaction-buttons--emoji ion-button.selected{transform:scale(1.25)}.reaction-buttons--emoji ion-button:not(.selected):not([disabled]){opacity:.55}.reaction-buttons--emoji ion-button:not(.selected):not([disabled]):hover{opacity:1}.emoji-btn{font-size:2rem;line-height:1;display:block}.comment-section{margin-top:16px;animation:slideIn .3s ease-out}.comment-section .comment-textarea{--background: transparent;--border-radius: 8px;--padding-start: 12px;--padding-end: 12px;width:100%}.comment-section .char-count{display:block;text-align:right;font-size:.75rem;color:var(--ion-color-medium);margin-top:4px}.submit-button{margin-top:16px}.submitted-message{margin-top:16px;color:var(--ion-color-success);font-weight:500;animation:fadeIn .3s ease-out}.error-message{margin-top:8px;color:var(--ion-color-danger);font-size:.875rem}.loading-container{display:flex;justify-content:center;padding:20px}@keyframes slideIn{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { 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: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { kind: "component", type: IonTextarea, selector: "ion-textarea", inputs: ["autoGrow", "autocapitalize", "autofocus", "clearOnEdit", "color", "cols", "counter", "counterFormatter", "debounce", "disabled", "enterkeyhint", "errorText", "fill", "helperText", "inputmode", "label", "labelPlacement", "maxlength", "minlength", "mode", "name", "placeholder", "readonly", "required", "rows", "shape", "spellcheck", "value", "wrap"] }] }); }
|
|
55582
55596
|
}
|
|
55583
55597
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ContentReactionComponent, decorators: [{
|
|
55584
55598
|
type: Component,
|
|
55585
|
-
args: [{ selector: 'val-content-reaction', standalone: true, imports: [CommonModule, FormsModule, IonButton,
|
|
55599
|
+
args: [{ selector: 'val-content-reaction', standalone: true, imports: [CommonModule, FormsModule, IonButton, IonSpinner, IonTextarea], template: "@if (isConfigured()) {\n<div class=\"content-reaction\" [class.disabled]=\"resolvedProps().disabled\" [class.readonly]=\"resolvedProps().readonly\">\n <!-- Bot\u00F3n de cierre (solo en modo modal/sheet) -->\n @if (resolvedProps().showCloseButton) {\n <ion-button fill=\"clear\" color=\"dark\" shape=\"round\" class=\"close-button\" (click)=\"close()\">\n <strong>{{ t('close') }}</strong>\n </ion-button>\n }\n <!-- Loading inicial -->\n @if (state().isLoading && !state().selectedValue) {\n <div class=\"loading-container\">\n <ion-spinner name=\"crescent\"></ion-spinner>\n </div>\n } @else {\n <!-- Pregunta -->\n <p class=\"question\">\n {{ state().selectedValue === 'negative' ? t('negativeFeedbackTitle') : resolvedProps().question }}\n </p>\n\n <!-- Botones de reacci\u00F3n \u2014 variante emoji (caras, estrellas, thumbs, \u2026) -->\n @if (resolvedProps().variant === 'emoji') {\n <div class=\"reaction-buttons reaction-buttons--emoji\">\n @for (value of activeReactionValues(); track value; let i = $index) {\n <ion-button\n fill=\"clear\"\n shape=\"round\"\n [class.selected]=\"isSelected(value)\"\n [attr.aria-label]=\"resolvedProps().emojiLabels[i]\"\n [attr.aria-pressed]=\"isSelected(value)\"\n [disabled]=\"resolvedProps().disabled || resolvedProps().readonly\"\n (click)=\"selectReaction(value)\"\n >\n <span class=\"emoji-btn\">{{ resolvedProps().emojis[i] }}</span>\n </ion-button>\n }\n </div>\n } @else {\n <!-- Botones de reacci\u00F3n \u2014 variante texto (default) -->\n <div class=\"reaction-buttons\">\n <ion-button\n [fill]=\"isSelected('positive') ? 'solid' : 'outline'\"\n shape=\"round\"\n color=\"dark\"\n [attr.aria-pressed]=\"isSelected('positive')\"\n [disabled]=\"resolvedProps().disabled || resolvedProps().readonly\"\n (click)=\"selectReaction('positive')\"\n >\n {{ t('positiveLabel') }}\n </ion-button>\n <ion-button\n [fill]=\"isSelected('negative') ? 'solid' : 'outline'\"\n shape=\"round\"\n color=\"dark\"\n [attr.aria-pressed]=\"isSelected('negative')\"\n [disabled]=\"resolvedProps().disabled || resolvedProps().readonly\"\n (click)=\"selectReaction('negative')\"\n >\n {{ t('negativeLabel') }}\n </ion-button>\n </div>\n }\n\n <!-- Campo de comentario (solo si hay selecci\u00F3n) -->\n @if (showCommentField()) {\n <div class=\"comment-section\">\n <ion-textarea\n [value]=\"state().comment\"\n [placeholder]=\"resolvedProps().commentPlaceholder\"\n [maxlength]=\"resolvedProps().maxCommentLength\"\n [disabled]=\"resolvedProps().disabled\"\n [rows]=\"3\"\n class=\"comment-textarea\"\n (ionInput)=\"updateComment($event)\"\n ></ion-textarea>\n <span class=\"char-count\"> {{ state().comment.length }}/{{ resolvedProps().maxCommentLength }} </span>\n </div>\n }\n\n <!-- Bot\u00F3n de env\u00EDo -->\n @if (state().selectedValue && !state().isSubmitted) {\n <ion-button expand=\"block\" shape=\"round\" [disabled]=\"!canSubmit()\" (click)=\"submitReaction()\" class=\"submit-button\">\n @if (state().isLoading) {\n <ion-spinner name=\"crescent\"></ion-spinner>\n } @else { {{ state().hadPreviousReaction ? t('update') : t('submit') }} }\n </ion-button>\n }\n\n <!-- Mensaje de confirmaci\u00F3n -->\n @if (state().isSubmitted) {\n <p class=\"submitted-message\">{{ t('thankYou') }}</p>\n }\n\n <!-- Error -->\n @if (state().error) {\n <p class=\"error-message\">{{ state().error }}</p>\n } }\n</div>\n}\n", styles: [":host{display:block}.content-reaction{position:relative;padding:16px;text-align:center}.content-reaction.disabled{opacity:.6;pointer-events:none}.content-reaction.readonly{pointer-events:none}.close-button{position:absolute;top:0;right:0;margin:0;--padding-start: 6px;--padding-end: 6px;--color: var(--ion-color-medium)}.close-button ion-icon{font-size:1.5rem}.question{font-size:1.0625rem;font-weight:700;color:var(--ion-color-dark);margin:0 0 16px}.reaction-buttons{display:flex;justify-content:center;gap:12px;margin-bottom:20px}.reaction-buttons--emoji{gap:8px}.reaction-buttons--emoji ion-button{--padding-start: 4px;--padding-end: 4px;transition:transform .15s ease}.reaction-buttons--emoji ion-button.selected{transform:scale(1.25)}.reaction-buttons--emoji ion-button:not(.selected):not([disabled]){opacity:.55}.reaction-buttons--emoji ion-button:not(.selected):not([disabled]):hover{opacity:1}.emoji-btn{font-size:2rem;line-height:1;display:block}.comment-section{margin-top:16px;animation:slideIn .3s ease-out}.comment-section .comment-textarea{--background: transparent;--border-radius: 8px;--padding-start: 12px;--padding-end: 12px;width:100%}.comment-section .char-count{display:block;text-align:right;font-size:.75rem;color:var(--ion-color-medium);margin-top:4px}.submit-button{margin-top:16px}.submitted-message{margin-top:16px;color:var(--ion-color-success);font-weight:500;animation:fadeIn .3s ease-out}.error-message{margin-top:8px;color:var(--ion-color-danger);font-size:.875rem}.loading-container{display:flex;justify-content:center;padding:20px}@keyframes slideIn{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}\n"] }]
|
|
55586
55600
|
}], ctorParameters: () => [], propDecorators: { props: [{
|
|
55587
55601
|
type: Input
|
|
55588
55602
|
}], _modalRef: [{
|
|
@@ -56605,7 +56619,7 @@ class ApiKeysModalComponent {
|
|
|
56605
56619
|
<ion-header>
|
|
56606
56620
|
<ion-toolbar>
|
|
56607
56621
|
<ion-buttons slot="end">
|
|
56608
|
-
<ion-button fill="clear" color="dark" (click)="dismiss()">
|
|
56622
|
+
<ion-button fill="clear" color="dark" shape="round" (click)="dismiss()">
|
|
56609
56623
|
<strong>{{ t('close') }}</strong>
|
|
56610
56624
|
</ion-button>
|
|
56611
56625
|
</ion-buttons>
|
|
@@ -56625,10 +56639,10 @@ class ApiKeysModalComponent {
|
|
|
56625
56639
|
<code>{{ s.secret }}</code>
|
|
56626
56640
|
</div>
|
|
56627
56641
|
<div class="apikey-actions">
|
|
56628
|
-
<ion-button expand="block" color="dark" (click)="copy(s.secret)">
|
|
56642
|
+
<ion-button expand="block" color="dark" shape="round" (click)="copy(s.secret)">
|
|
56629
56643
|
{{ copied() ? t('copied') : t('copy') }}
|
|
56630
56644
|
</ion-button>
|
|
56631
|
-
<ion-button expand="block" fill="outline" color="dark" (click)="finishSecret()">
|
|
56645
|
+
<ion-button expand="block" fill="outline" color="dark" shape="round" (click)="finishSecret()">
|
|
56632
56646
|
{{ t('done') }}
|
|
56633
56647
|
</ion-button>
|
|
56634
56648
|
</div>
|
|
@@ -56651,7 +56665,7 @@ class ApiKeysModalComponent {
|
|
|
56651
56665
|
{{ k.lastUsedAt || t('never') }}
|
|
56652
56666
|
</span>
|
|
56653
56667
|
</div>
|
|
56654
|
-
<ion-button fill="clear" color="danger" size="small" (click)="revoke(k)">
|
|
56668
|
+
<ion-button fill="clear" color="danger" size="small" shape="round" (click)="revoke(k)">
|
|
56655
56669
|
{{ t('revoke') }}
|
|
56656
56670
|
</ion-button>
|
|
56657
56671
|
</div>
|
|
@@ -56674,13 +56688,13 @@ class ApiKeysModalComponent {
|
|
|
56674
56688
|
(selectedChange)="selectedPerms.set($event)"
|
|
56675
56689
|
/>
|
|
56676
56690
|
<val-form [props]="createForm()" (onSubmit)="onCreateSubmit($event)" />
|
|
56677
|
-
<ion-button expand="block" fill="clear" color="medium" (click)="showCreate.set(false)">
|
|
56691
|
+
<ion-button expand="block" fill="clear" color="medium" shape="round" (click)="showCreate.set(false)">
|
|
56678
56692
|
{{ t('cancel') }}
|
|
56679
56693
|
</ion-button>
|
|
56680
56694
|
</div>
|
|
56681
56695
|
} @else {
|
|
56682
56696
|
<div class="apikey-actions">
|
|
56683
|
-
<ion-button expand="block" color="primary" (click)="openCreate()">
|
|
56697
|
+
<ion-button expand="block" color="primary" shape="round" (click)="openCreate()">
|
|
56684
56698
|
{{ t('newKey') }}
|
|
56685
56699
|
</ion-button>
|
|
56686
56700
|
</div>
|
|
@@ -56708,7 +56722,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
56708
56722
|
<ion-header>
|
|
56709
56723
|
<ion-toolbar>
|
|
56710
56724
|
<ion-buttons slot="end">
|
|
56711
|
-
<ion-button fill="clear" color="dark" (click)="dismiss()">
|
|
56725
|
+
<ion-button fill="clear" color="dark" shape="round" (click)="dismiss()">
|
|
56712
56726
|
<strong>{{ t('close') }}</strong>
|
|
56713
56727
|
</ion-button>
|
|
56714
56728
|
</ion-buttons>
|
|
@@ -56728,10 +56742,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
56728
56742
|
<code>{{ s.secret }}</code>
|
|
56729
56743
|
</div>
|
|
56730
56744
|
<div class="apikey-actions">
|
|
56731
|
-
<ion-button expand="block" color="dark" (click)="copy(s.secret)">
|
|
56745
|
+
<ion-button expand="block" color="dark" shape="round" (click)="copy(s.secret)">
|
|
56732
56746
|
{{ copied() ? t('copied') : t('copy') }}
|
|
56733
56747
|
</ion-button>
|
|
56734
|
-
<ion-button expand="block" fill="outline" color="dark" (click)="finishSecret()">
|
|
56748
|
+
<ion-button expand="block" fill="outline" color="dark" shape="round" (click)="finishSecret()">
|
|
56735
56749
|
{{ t('done') }}
|
|
56736
56750
|
</ion-button>
|
|
56737
56751
|
</div>
|
|
@@ -56754,7 +56768,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
56754
56768
|
{{ k.lastUsedAt || t('never') }}
|
|
56755
56769
|
</span>
|
|
56756
56770
|
</div>
|
|
56757
|
-
<ion-button fill="clear" color="danger" size="small" (click)="revoke(k)">
|
|
56771
|
+
<ion-button fill="clear" color="danger" size="small" shape="round" (click)="revoke(k)">
|
|
56758
56772
|
{{ t('revoke') }}
|
|
56759
56773
|
</ion-button>
|
|
56760
56774
|
</div>
|
|
@@ -56777,13 +56791,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
56777
56791
|
(selectedChange)="selectedPerms.set($event)"
|
|
56778
56792
|
/>
|
|
56779
56793
|
<val-form [props]="createForm()" (onSubmit)="onCreateSubmit($event)" />
|
|
56780
|
-
<ion-button expand="block" fill="clear" color="medium" (click)="showCreate.set(false)">
|
|
56794
|
+
<ion-button expand="block" fill="clear" color="medium" shape="round" (click)="showCreate.set(false)">
|
|
56781
56795
|
{{ t('cancel') }}
|
|
56782
56796
|
</ion-button>
|
|
56783
56797
|
</div>
|
|
56784
56798
|
} @else {
|
|
56785
56799
|
<div class="apikey-actions">
|
|
56786
|
-
<ion-button expand="block" color="primary" (click)="openCreate()">
|
|
56800
|
+
<ion-button expand="block" color="primary" shape="round" (click)="openCreate()">
|
|
56787
56801
|
{{ t('newKey') }}
|
|
56788
56802
|
</ion-button>
|
|
56789
56803
|
</div>
|