valtech-components 4.0.27 → 4.0.29
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/esm2022/lib/components/organisms/account-view/account-view.component.mjs +34 -30
- package/esm2022/lib/components/organisms/change-password-modal/change-password-modal.component.mjs +3 -3
- package/esm2022/lib/components/organisms/create-org-modal/create-org-modal.component.mjs +91 -65
- package/esm2022/lib/components/organisms/delete-account-modal/delete-account-modal.component.mjs +106 -84
- package/esm2022/lib/components/organisms/login/login.component.mjs +11 -4
- package/esm2022/lib/components/organisms/login/types.mjs +23 -1
- package/esm2022/lib/components/organisms/organization-view/organization-view.component.mjs +16 -3
- package/esm2022/lib/components/organisms/permissions-view/permissions-modal.component.mjs +70 -0
- package/esm2022/lib/components/organisms/permissions-view/permissions-view.component.mjs +16 -5
- package/esm2022/lib/version.mjs +2 -2
- package/esm2022/public-api.mjs +2 -1
- package/fesm2022/valtech-components.mjs +1125 -956
- package/fesm2022/valtech-components.mjs.map +1 -1
- package/lib/components/organisms/account-view/account-view.component.d.ts +3 -0
- package/lib/components/organisms/create-org-modal/create-org-modal.component.d.ts +8 -8
- package/lib/components/organisms/delete-account-modal/delete-account-modal.component.d.ts +7 -8
- package/lib/components/organisms/login/login.component.d.ts +6 -0
- package/lib/components/organisms/login/types.d.ts +12 -0
- package/lib/components/organisms/organization-view/organization-view.component.d.ts +1 -0
- package/lib/components/organisms/permissions-view/permissions-modal.component.d.ts +13 -0
- package/lib/components/organisms/permissions-view/permissions-view.component.d.ts +9 -2
- package/lib/version.d.ts +1 -1
- package/package.json +1 -1
- package/public-api.d.ts +1 -0
- package/src/lib/services/firebase/firebase-messaging-sw.js +145 -0
|
@@ -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.29';
|
|
60
60
|
|
|
61
61
|
// Control de estado de refresco (singleton a nivel de módulo)
|
|
62
62
|
let isRefreshing = false;
|
|
@@ -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\" 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"] }] }); }
|
|
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()\" 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 <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\" 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"] }]
|
|
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()\" 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 <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: [{
|
|
@@ -40218,6 +40218,28 @@ const LOGIN_DEFAULTS = {
|
|
|
40218
40218
|
showForgotPassword: true,
|
|
40219
40219
|
showCard: false,
|
|
40220
40220
|
};
|
|
40221
|
+
/**
|
|
40222
|
+
* Logo por defecto de la card de login cuando el consumer no pasa `logo`.
|
|
40223
|
+
*
|
|
40224
|
+
* Usa el token CSS `--main-logo` (resuelto por `val-image` vía `var(--main-logo)`),
|
|
40225
|
+
* que cada app del factory define en su `theme/variables.scss` con su logo de
|
|
40226
|
+
* marca (incluye variante dark). Así el login muestra SIEMPRE el logo de la app
|
|
40227
|
+
* sin acoplar la lib a una marca concreta — es el estándar del factory.
|
|
40228
|
+
*
|
|
40229
|
+
* Una app puede override pasando su propio `logo` en `LoginMetadata` (ej.
|
|
40230
|
+
* showcase usa un mark propio `terminal.svg`).
|
|
40231
|
+
*/
|
|
40232
|
+
const DEFAULT_LOGIN_LOGO = {
|
|
40233
|
+
src: '--main-logo',
|
|
40234
|
+
alt: '',
|
|
40235
|
+
mode: 'box',
|
|
40236
|
+
shaded: false,
|
|
40237
|
+
bordered: false,
|
|
40238
|
+
size: 'xlarge',
|
|
40239
|
+
alignment: 'center',
|
|
40240
|
+
flex: true,
|
|
40241
|
+
width: 8,
|
|
40242
|
+
};
|
|
40221
40243
|
|
|
40222
40244
|
class LoginComponent {
|
|
40223
40245
|
constructor() {
|
|
@@ -40569,6 +40591,13 @@ class LoginComponent {
|
|
|
40569
40591
|
get config() {
|
|
40570
40592
|
return { ...LOGIN_DEFAULTS, ...this.props };
|
|
40571
40593
|
}
|
|
40594
|
+
/**
|
|
40595
|
+
* Logo a renderizar arriba del form. Si el consumer no pasa `logo`, cae al
|
|
40596
|
+
* default de marca del factory (`--main-logo` por app). Ver DEFAULT_LOGIN_LOGO.
|
|
40597
|
+
*/
|
|
40598
|
+
get resolvedLogo() {
|
|
40599
|
+
return this.props.logo ?? DEFAULT_LOGIN_LOGO;
|
|
40600
|
+
}
|
|
40572
40601
|
// ==========================================
|
|
40573
40602
|
// HANDLERS
|
|
40574
40603
|
// ==========================================
|
|
@@ -40930,7 +40959,7 @@ class LoginComponent {
|
|
|
40930
40959
|
this.stopResetResendCooldown();
|
|
40931
40960
|
}
|
|
40932
40961
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: LoginComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
40933
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: LoginComponent, isStandalone: true, selector: "val-login", inputs: { props: "props" }, outputs: { onSuccess: "onSuccess", onError: "onError", onMFARequired: "onMFARequired" }, ngImport: i0, template: "<div class=\"val-login\" [class.val-login--card]=\"config.showCard\">\n <!-- Logo -->\n @if (props.logo) {\n <div class=\"logo-container\">\n <val-image [props]=\"props.logo\" />\n </div>\n }\n\n <!-- Login Form -->\n <val-form [props]=\"loginFormProps()\" (onSubmit)=\"loginHandler($event)\" />\n\n <!-- OAuth Section -->\n @if (config.showOAuth && config.oauthProviders.length > 0) {\n <div class=\"oauth-separator\">\n <span>{{ t('orContinueWith') }}</span>\n </div>\n\n <div class=\"oauth-buttons\">\n @for (provider of config.oauthProviders; track provider) {\n <ion-button\n expand=\"block\"\n fill=\"outline\"\n color=\"dark\"\n (click)=\"loginWithOAuth(provider)\"\n [disabled]=\"isOAuthLoading\"\n >\n @switch (provider) { @case ('google') {\n <ion-icon slot=\"start\" name=\"logo-google\" aria-hidden=\"true\"></ion-icon>\n {{ isOAuthLoading ? t('connecting') : t('continueWithGoogle') }} } @case ('apple') {\n <ion-icon slot=\"start\" name=\"logo-apple\" aria-hidden=\"true\"></ion-icon>\n {{ isOAuthLoading ? t('connecting') : t('continueWithApple') }} } @case ('microsoft') {\n <ion-icon slot=\"start\" name=\"logo-microsoft\" aria-hidden=\"true\"></ion-icon>\n {{ isOAuthLoading ? t('connecting') : t('continueWithMicrosoft') }} } }\n </ion-button>\n }\n </div>\n }\n\n <!-- Register Link -->\n @if (config.showRegister) {\n <div class=\"auth-link\">\n <ion-text color=\"dark\">\n {{ t('noAccount') }}\n <a (click)=\"openRegisterModal()\">{{ t('register') }}</a>\n </ion-text>\n </div>\n }\n\n <!-- Forgot Password Link -->\n @if (config.showForgotPassword) {\n <div class=\"auth-link forgot-password\">\n <ion-text color=\"dark\">\n {{ t('forgotLink') }}\n <a (click)=\"openForgotPasswordModal()\">{{ t('recoverPassword') }}</a>\n </ion-text>\n </div>\n }\n\n <!-- Legal Notice -->\n @if (props.legal) {\n <div class=\"legal-notice\">\n <ion-text color=\"dark\">\n <p>\n {{ t('legalPrefix') }} @if (resolvedCompanyLink) {\n <a [href]=\"resolvedCompanyLink\" target=\"_blank\" rel=\"noopener noreferrer\"\n ><strong>{{ props.legal.companyName }}</strong></a\n >\n } @else {\n <strong>{{ props.legal.companyName }}</strong>\n } {{ t('legalSuffix') }} @if (props.legal.termsLink) {\n <a [href]=\"props.legal.termsLink\">{{ t('termsAndConditions') }}</a>\n } @else {\n <span>{{ t('termsAndConditions') }}</span>\n } {{ t('and') }} @if (props.legal.privacyLink) {\n <a [href]=\"props.legal.privacyLink\">{{ t('privacyPolicy') }}</a>\n } @else {\n <span>{{ t('privacyPolicy') }}</span>\n }.\n </p>\n </ion-text>\n </div>\n }\n</div>\n\n<!-- Register Modal -->\n<ion-modal [isOpen]=\"isRegisterModalOpen\" (didDismiss)=\"closeRegisterModal()\">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-buttons slot=\"end\">\n <ion-button fill=\"clear\" [attr.aria-label]=\"t('close')\" (click)=\"closeRegisterModal()\">\n <ion-icon name=\"close-outline\" aria-hidden=\"true\"></ion-icon>\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 (props.logo) {\n <div class=\"logo-container\">\n <val-image [props]=\"props.logo\" />\n </div>\n }\n <val-form [props]=\"registerFormProps()\" (onSubmit)=\"registerHandler($event)\" />\n <div class=\"auth-link\">\n <ion-text color=\"dark\">\n {{ t('hasAccount') }}\n <a (click)=\"closeRegisterModal()\">{{ t('signIn') }}</a>\n </ion-text>\n </div>\n </section>\n </ion-content>\n </ng-template>\n</ion-modal>\n\n<!-- Verify Email Modal -->\n<ion-modal [isOpen]=\"isVerifyModalOpen\" [backdropDismiss]=\"false\">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-buttons slot=\"end\">\n <ion-button fill=\"clear\" [attr.aria-label]=\"t('close')\" (click)=\"closeVerifyModal()\">\n <ion-icon name=\"close-outline\" aria-hidden=\"true\"></ion-icon>\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 (props.logo) {\n <div class=\"logo-container\">\n <val-image [props]=\"props.logo\" />\n </div>\n }\n <val-form [props]=\"verifyFormProps()\" (onSubmit)=\"verifyHandler($event)\" />\n <div class=\"resend-link\">\n <ion-text color=\"dark\">\n {{ t('noCodeReceived') }} @if (resendCooldown > 0) {\n <span class=\"cooldown\">{{ t('resendIn', { seconds: resendCooldown.toString() }) }}</span>\n } @else {\n <a (click)=\"resendCode()\">{{ t('resend') }}</a>\n }\n </ion-text>\n </div>\n </section>\n </ion-content>\n </ng-template>\n</ion-modal>\n\n<!-- Forgot Password Modal -->\n<ion-modal [isOpen]=\"isForgotPasswordModalOpen\" (didDismiss)=\"closeForgotPasswordModal()\">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-buttons slot=\"end\">\n <ion-button fill=\"clear\" [attr.aria-label]=\"t('close')\" (click)=\"closeForgotPasswordModal()\">\n <ion-icon name=\"close-outline\" aria-hidden=\"true\"></ion-icon>\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 (props.logo) {\n <div class=\"logo-container\">\n <val-image [props]=\"props.logo\" />\n </div>\n }\n <val-form [props]=\"forgotPasswordFormProps()\" (onSubmit)=\"forgotPasswordHandler($event)\" />\n </section>\n </ion-content>\n </ng-template>\n</ion-modal>\n\n<!-- Reset Password Modal -->\n<ion-modal [isOpen]=\"isResetPasswordModalOpen\" [backdropDismiss]=\"false\">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-buttons slot=\"end\">\n <ion-button fill=\"clear\" [attr.aria-label]=\"t('close')\" (click)=\"closeResetPasswordModal()\">\n <ion-icon name=\"close-outline\" aria-hidden=\"true\"></ion-icon>\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 (props.logo) {\n <div class=\"logo-container\">\n <val-image [props]=\"props.logo\" />\n </div>\n }\n <val-form [props]=\"resetPasswordFormProps()\" (onSubmit)=\"resetPasswordHandler($event)\" />\n <div class=\"resend-link\">\n <ion-text color=\"dark\">\n {{ t('noCodeReceived') }} @if (resetResendCooldown > 0) {\n <span class=\"cooldown\">{{ t('resendIn', { seconds: resetResendCooldown.toString() }) }}</span>\n } @else {\n <a (click)=\"resendResetCode()\">{{ t('resend') }}</a>\n }\n </ion-text>\n </div>\n </section>\n </ion-content>\n </ng-template>\n</ion-modal>\n\n<!-- MFA Verify Modal -->\n<ion-modal [isOpen]=\"isMFAVerifyModalOpen\" [backdropDismiss]=\"false\" (didDismiss)=\"onMFAVerifyDismissed()\">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-buttons slot=\"end\">\n <ion-button fill=\"clear\" [attr.aria-label]=\"t('close')\" (click)=\"closeMFAVerifyModal()\">\n <ion-icon name=\"close-outline\" aria-hidden=\"true\"></ion-icon>\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 (props.logo) {\n <div class=\"logo-container\">\n <val-image [props]=\"props.logo\" />\n </div>\n }\n <val-form [props]=\"mfaVerifyFormProps()\" (onSubmit)=\"verifyMFAHandler($event)\" />\n </section>\n </ion-content>\n </ng-template>\n</ion-modal>\n", styles: [".val-login{width:100%}.val-login--card{padding:1rem .75rem;background:var(--ion-color-light);border-radius:16px;box-shadow:0 2px 8px #00000014}.logo-container{max-width:130px;margin-bottom:1.5rem}.auth-link{text-align:center;margin-top:1rem;font-size:.9rem}.auth-link a{color:var(--ion-color-primary);text-decoration:none;font-weight:500;cursor:pointer}.auth-link a:hover{text-decoration:underline}.oauth-separator{display:flex;align-items:center;margin:1.5rem 0}.oauth-separator:before,.oauth-separator:after{content:\"\";flex:1;height:1px;background:var(--ion-color-medium-tint)}.oauth-separator span{padding:0 1rem;color:var(--ion-color-dark);font-size:.85rem}.oauth-buttons{margin-bottom:1rem}.oauth-buttons ion-button{--border-radius: 8px;--border-width: 1px}.oauth-buttons ion-icon{font-size:1.2rem;margin-right:.5rem}.legal-notice{margin-top:1.5rem;padding-top:1rem;border-top:1px solid var(--ion-color-medium-tint)}.legal-notice p{font-size:.75rem;line-height:1.5;text-align:center;margin:0}.legal-notice a{color:var(--ion-color-primary);text-decoration:none}.legal-notice a:hover{text-decoration:underline}.resend-link{text-align:center;margin-top:1rem;font-size:.9rem}.resend-link a{color:var(--ion-color-primary);cursor:pointer;font-weight:500}.resend-link a:hover{text-decoration:underline}.resend-link .cooldown{color:var(--ion-color-medium)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { 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: IonModal, selector: "ion-modal" }, { kind: "component", type: IonText, selector: "ion-text", inputs: ["color", "mode"] }, { kind: "component", type: IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "component", type: FormComponent, selector: "val-form", inputs: ["props"], outputs: ["onSubmit", "onInvalid", "onSelectChange"] }, { kind: "component", type: ImageComponent, selector: "val-image", inputs: ["props"] }] }); }
|
|
40962
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: LoginComponent, isStandalone: true, selector: "val-login", inputs: { props: "props" }, outputs: { onSuccess: "onSuccess", onError: "onError", onMFARequired: "onMFARequired" }, ngImport: i0, template: "<div class=\"val-login\" [class.val-login--card]=\"config.showCard\">\n <!-- Logo \u2014 default de marca (--main-logo) si el consumer no pasa logo -->\n <div class=\"logo-container\">\n <val-image [props]=\"resolvedLogo\" />\n </div>\n\n <!-- Login Form -->\n <val-form [props]=\"loginFormProps()\" (onSubmit)=\"loginHandler($event)\" />\n\n <!-- OAuth Section -->\n @if (config.showOAuth && config.oauthProviders.length > 0) {\n <div class=\"oauth-separator\">\n <span>{{ t('orContinueWith') }}</span>\n </div>\n\n <div class=\"oauth-buttons\">\n @for (provider of config.oauthProviders; track provider) {\n <ion-button\n expand=\"block\"\n fill=\"outline\"\n color=\"dark\"\n (click)=\"loginWithOAuth(provider)\"\n [disabled]=\"isOAuthLoading\"\n >\n @switch (provider) { @case ('google') {\n <ion-icon slot=\"start\" name=\"logo-google\" aria-hidden=\"true\"></ion-icon>\n {{ isOAuthLoading ? t('connecting') : t('continueWithGoogle') }} } @case ('apple') {\n <ion-icon slot=\"start\" name=\"logo-apple\" aria-hidden=\"true\"></ion-icon>\n {{ isOAuthLoading ? t('connecting') : t('continueWithApple') }} } @case ('microsoft') {\n <ion-icon slot=\"start\" name=\"logo-microsoft\" aria-hidden=\"true\"></ion-icon>\n {{ isOAuthLoading ? t('connecting') : t('continueWithMicrosoft') }} } }\n </ion-button>\n }\n </div>\n }\n\n <!-- Register Link -->\n @if (config.showRegister) {\n <div class=\"auth-link\">\n <ion-text color=\"dark\">\n {{ t('noAccount') }}\n <a (click)=\"openRegisterModal()\">{{ t('register') }}</a>\n </ion-text>\n </div>\n }\n\n <!-- Forgot Password Link -->\n @if (config.showForgotPassword) {\n <div class=\"auth-link forgot-password\">\n <ion-text color=\"dark\">\n {{ t('forgotLink') }}\n <a (click)=\"openForgotPasswordModal()\">{{ t('recoverPassword') }}</a>\n </ion-text>\n </div>\n }\n\n <!-- Legal Notice -->\n @if (props.legal) {\n <div class=\"legal-notice\">\n <ion-text color=\"dark\">\n <p>\n {{ t('legalPrefix') }} @if (resolvedCompanyLink) {\n <a [href]=\"resolvedCompanyLink\" target=\"_blank\" rel=\"noopener noreferrer\"\n ><strong>{{ props.legal.companyName }}</strong></a\n >\n } @else {\n <strong>{{ props.legal.companyName }}</strong>\n } {{ t('legalSuffix') }} @if (props.legal.termsLink) {\n <a [href]=\"props.legal.termsLink\">{{ t('termsAndConditions') }}</a>\n } @else {\n <span>{{ t('termsAndConditions') }}</span>\n } {{ t('and') }} @if (props.legal.privacyLink) {\n <a [href]=\"props.legal.privacyLink\">{{ t('privacyPolicy') }}</a>\n } @else {\n <span>{{ t('privacyPolicy') }}</span>\n }.\n </p>\n </ion-text>\n </div>\n }\n</div>\n\n<!-- Register Modal -->\n<ion-modal [isOpen]=\"isRegisterModalOpen\" (didDismiss)=\"closeRegisterModal()\">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-buttons slot=\"end\">\n <ion-button fill=\"clear\" [attr.aria-label]=\"t('close')\" (click)=\"closeRegisterModal()\">\n <ion-icon name=\"close-outline\" aria-hidden=\"true\"></ion-icon>\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 <div class=\"logo-container\">\n <val-image [props]=\"resolvedLogo\" />\n </div>\n <val-form [props]=\"registerFormProps()\" (onSubmit)=\"registerHandler($event)\" />\n <div class=\"auth-link\">\n <ion-text color=\"dark\">\n {{ t('hasAccount') }}\n <a (click)=\"closeRegisterModal()\">{{ t('signIn') }}</a>\n </ion-text>\n </div>\n </section>\n </ion-content>\n </ng-template>\n</ion-modal>\n\n<!-- Verify Email Modal -->\n<ion-modal [isOpen]=\"isVerifyModalOpen\" [backdropDismiss]=\"false\">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-buttons slot=\"end\">\n <ion-button fill=\"clear\" [attr.aria-label]=\"t('close')\" (click)=\"closeVerifyModal()\">\n <ion-icon name=\"close-outline\" aria-hidden=\"true\"></ion-icon>\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 <div class=\"logo-container\">\n <val-image [props]=\"resolvedLogo\" />\n </div>\n <val-form [props]=\"verifyFormProps()\" (onSubmit)=\"verifyHandler($event)\" />\n <div class=\"resend-link\">\n <ion-text color=\"dark\">\n {{ t('noCodeReceived') }} @if (resendCooldown > 0) {\n <span class=\"cooldown\">{{ t('resendIn', { seconds: resendCooldown.toString() }) }}</span>\n } @else {\n <a (click)=\"resendCode()\">{{ t('resend') }}</a>\n }\n </ion-text>\n </div>\n </section>\n </ion-content>\n </ng-template>\n</ion-modal>\n\n<!-- Forgot Password Modal -->\n<ion-modal [isOpen]=\"isForgotPasswordModalOpen\" (didDismiss)=\"closeForgotPasswordModal()\">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-buttons slot=\"end\">\n <ion-button fill=\"clear\" [attr.aria-label]=\"t('close')\" (click)=\"closeForgotPasswordModal()\">\n <ion-icon name=\"close-outline\" aria-hidden=\"true\"></ion-icon>\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 <div class=\"logo-container\">\n <val-image [props]=\"resolvedLogo\" />\n </div>\n <val-form [props]=\"forgotPasswordFormProps()\" (onSubmit)=\"forgotPasswordHandler($event)\" />\n </section>\n </ion-content>\n </ng-template>\n</ion-modal>\n\n<!-- Reset Password Modal -->\n<ion-modal [isOpen]=\"isResetPasswordModalOpen\" [backdropDismiss]=\"false\">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-buttons slot=\"end\">\n <ion-button fill=\"clear\" [attr.aria-label]=\"t('close')\" (click)=\"closeResetPasswordModal()\">\n <ion-icon name=\"close-outline\" aria-hidden=\"true\"></ion-icon>\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 <div class=\"logo-container\">\n <val-image [props]=\"resolvedLogo\" />\n </div>\n <val-form [props]=\"resetPasswordFormProps()\" (onSubmit)=\"resetPasswordHandler($event)\" />\n <div class=\"resend-link\">\n <ion-text color=\"dark\">\n {{ t('noCodeReceived') }} @if (resetResendCooldown > 0) {\n <span class=\"cooldown\">{{ t('resendIn', { seconds: resetResendCooldown.toString() }) }}</span>\n } @else {\n <a (click)=\"resendResetCode()\">{{ t('resend') }}</a>\n }\n </ion-text>\n </div>\n </section>\n </ion-content>\n </ng-template>\n</ion-modal>\n\n<!-- MFA Verify Modal -->\n<ion-modal [isOpen]=\"isMFAVerifyModalOpen\" [backdropDismiss]=\"false\" (didDismiss)=\"onMFAVerifyDismissed()\">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-buttons slot=\"end\">\n <ion-button fill=\"clear\" [attr.aria-label]=\"t('close')\" (click)=\"closeMFAVerifyModal()\">\n <ion-icon name=\"close-outline\" aria-hidden=\"true\"></ion-icon>\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 <div class=\"logo-container\">\n <val-image [props]=\"resolvedLogo\" />\n </div>\n <val-form [props]=\"mfaVerifyFormProps()\" (onSubmit)=\"verifyMFAHandler($event)\" />\n </section>\n </ion-content>\n </ng-template>\n</ion-modal>\n", styles: [".val-login{width:100%}.val-login--card{padding:1rem .75rem;background:var(--ion-color-light);border-radius:16px;box-shadow:0 2px 8px #00000014}.logo-container{max-width:130px;margin-bottom:1.5rem}.auth-link{text-align:center;margin-top:1rem;font-size:.9rem}.auth-link a{color:var(--ion-color-primary);text-decoration:none;font-weight:500;cursor:pointer}.auth-link a:hover{text-decoration:underline}.oauth-separator{display:flex;align-items:center;margin:1.5rem 0}.oauth-separator:before,.oauth-separator:after{content:\"\";flex:1;height:1px;background:var(--ion-color-medium-tint)}.oauth-separator span{padding:0 1rem;color:var(--ion-color-dark);font-size:.85rem}.oauth-buttons{margin-bottom:1rem}.oauth-buttons ion-button{--border-radius: 8px;--border-width: 1px}.oauth-buttons ion-icon{font-size:1.2rem;margin-right:.5rem}.legal-notice{margin-top:1.5rem;padding-top:1rem;border-top:1px solid var(--ion-color-medium-tint)}.legal-notice p{font-size:.75rem;line-height:1.5;text-align:center;margin:0}.legal-notice a{color:var(--ion-color-primary);text-decoration:none}.legal-notice a:hover{text-decoration:underline}.resend-link{text-align:center;margin-top:1rem;font-size:.9rem}.resend-link a{color:var(--ion-color-primary);cursor:pointer;font-weight:500}.resend-link a:hover{text-decoration:underline}.resend-link .cooldown{color:var(--ion-color-medium)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { 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: IonModal, selector: "ion-modal" }, { kind: "component", type: IonText, selector: "ion-text", inputs: ["color", "mode"] }, { kind: "component", type: IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "component", type: FormComponent, selector: "val-form", inputs: ["props"], outputs: ["onSubmit", "onInvalid", "onSelectChange"] }, { kind: "component", type: ImageComponent, selector: "val-image", inputs: ["props"] }] }); }
|
|
40934
40963
|
}
|
|
40935
40964
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: LoginComponent, decorators: [{
|
|
40936
40965
|
type: Component,
|
|
@@ -40946,7 +40975,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
40946
40975
|
IonToolbar,
|
|
40947
40976
|
FormComponent,
|
|
40948
40977
|
ImageComponent,
|
|
40949
|
-
], template: "<div class=\"val-login\" [class.val-login--card]=\"config.showCard\">\n <!-- Logo
|
|
40978
|
+
], template: "<div class=\"val-login\" [class.val-login--card]=\"config.showCard\">\n <!-- Logo \u2014 default de marca (--main-logo) si el consumer no pasa logo -->\n <div class=\"logo-container\">\n <val-image [props]=\"resolvedLogo\" />\n </div>\n\n <!-- Login Form -->\n <val-form [props]=\"loginFormProps()\" (onSubmit)=\"loginHandler($event)\" />\n\n <!-- OAuth Section -->\n @if (config.showOAuth && config.oauthProviders.length > 0) {\n <div class=\"oauth-separator\">\n <span>{{ t('orContinueWith') }}</span>\n </div>\n\n <div class=\"oauth-buttons\">\n @for (provider of config.oauthProviders; track provider) {\n <ion-button\n expand=\"block\"\n fill=\"outline\"\n color=\"dark\"\n (click)=\"loginWithOAuth(provider)\"\n [disabled]=\"isOAuthLoading\"\n >\n @switch (provider) { @case ('google') {\n <ion-icon slot=\"start\" name=\"logo-google\" aria-hidden=\"true\"></ion-icon>\n {{ isOAuthLoading ? t('connecting') : t('continueWithGoogle') }} } @case ('apple') {\n <ion-icon slot=\"start\" name=\"logo-apple\" aria-hidden=\"true\"></ion-icon>\n {{ isOAuthLoading ? t('connecting') : t('continueWithApple') }} } @case ('microsoft') {\n <ion-icon slot=\"start\" name=\"logo-microsoft\" aria-hidden=\"true\"></ion-icon>\n {{ isOAuthLoading ? t('connecting') : t('continueWithMicrosoft') }} } }\n </ion-button>\n }\n </div>\n }\n\n <!-- Register Link -->\n @if (config.showRegister) {\n <div class=\"auth-link\">\n <ion-text color=\"dark\">\n {{ t('noAccount') }}\n <a (click)=\"openRegisterModal()\">{{ t('register') }}</a>\n </ion-text>\n </div>\n }\n\n <!-- Forgot Password Link -->\n @if (config.showForgotPassword) {\n <div class=\"auth-link forgot-password\">\n <ion-text color=\"dark\">\n {{ t('forgotLink') }}\n <a (click)=\"openForgotPasswordModal()\">{{ t('recoverPassword') }}</a>\n </ion-text>\n </div>\n }\n\n <!-- Legal Notice -->\n @if (props.legal) {\n <div class=\"legal-notice\">\n <ion-text color=\"dark\">\n <p>\n {{ t('legalPrefix') }} @if (resolvedCompanyLink) {\n <a [href]=\"resolvedCompanyLink\" target=\"_blank\" rel=\"noopener noreferrer\"\n ><strong>{{ props.legal.companyName }}</strong></a\n >\n } @else {\n <strong>{{ props.legal.companyName }}</strong>\n } {{ t('legalSuffix') }} @if (props.legal.termsLink) {\n <a [href]=\"props.legal.termsLink\">{{ t('termsAndConditions') }}</a>\n } @else {\n <span>{{ t('termsAndConditions') }}</span>\n } {{ t('and') }} @if (props.legal.privacyLink) {\n <a [href]=\"props.legal.privacyLink\">{{ t('privacyPolicy') }}</a>\n } @else {\n <span>{{ t('privacyPolicy') }}</span>\n }.\n </p>\n </ion-text>\n </div>\n }\n</div>\n\n<!-- Register Modal -->\n<ion-modal [isOpen]=\"isRegisterModalOpen\" (didDismiss)=\"closeRegisterModal()\">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-buttons slot=\"end\">\n <ion-button fill=\"clear\" [attr.aria-label]=\"t('close')\" (click)=\"closeRegisterModal()\">\n <ion-icon name=\"close-outline\" aria-hidden=\"true\"></ion-icon>\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 <div class=\"logo-container\">\n <val-image [props]=\"resolvedLogo\" />\n </div>\n <val-form [props]=\"registerFormProps()\" (onSubmit)=\"registerHandler($event)\" />\n <div class=\"auth-link\">\n <ion-text color=\"dark\">\n {{ t('hasAccount') }}\n <a (click)=\"closeRegisterModal()\">{{ t('signIn') }}</a>\n </ion-text>\n </div>\n </section>\n </ion-content>\n </ng-template>\n</ion-modal>\n\n<!-- Verify Email Modal -->\n<ion-modal [isOpen]=\"isVerifyModalOpen\" [backdropDismiss]=\"false\">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-buttons slot=\"end\">\n <ion-button fill=\"clear\" [attr.aria-label]=\"t('close')\" (click)=\"closeVerifyModal()\">\n <ion-icon name=\"close-outline\" aria-hidden=\"true\"></ion-icon>\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 <div class=\"logo-container\">\n <val-image [props]=\"resolvedLogo\" />\n </div>\n <val-form [props]=\"verifyFormProps()\" (onSubmit)=\"verifyHandler($event)\" />\n <div class=\"resend-link\">\n <ion-text color=\"dark\">\n {{ t('noCodeReceived') }} @if (resendCooldown > 0) {\n <span class=\"cooldown\">{{ t('resendIn', { seconds: resendCooldown.toString() }) }}</span>\n } @else {\n <a (click)=\"resendCode()\">{{ t('resend') }}</a>\n }\n </ion-text>\n </div>\n </section>\n </ion-content>\n </ng-template>\n</ion-modal>\n\n<!-- Forgot Password Modal -->\n<ion-modal [isOpen]=\"isForgotPasswordModalOpen\" (didDismiss)=\"closeForgotPasswordModal()\">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-buttons slot=\"end\">\n <ion-button fill=\"clear\" [attr.aria-label]=\"t('close')\" (click)=\"closeForgotPasswordModal()\">\n <ion-icon name=\"close-outline\" aria-hidden=\"true\"></ion-icon>\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 <div class=\"logo-container\">\n <val-image [props]=\"resolvedLogo\" />\n </div>\n <val-form [props]=\"forgotPasswordFormProps()\" (onSubmit)=\"forgotPasswordHandler($event)\" />\n </section>\n </ion-content>\n </ng-template>\n</ion-modal>\n\n<!-- Reset Password Modal -->\n<ion-modal [isOpen]=\"isResetPasswordModalOpen\" [backdropDismiss]=\"false\">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-buttons slot=\"end\">\n <ion-button fill=\"clear\" [attr.aria-label]=\"t('close')\" (click)=\"closeResetPasswordModal()\">\n <ion-icon name=\"close-outline\" aria-hidden=\"true\"></ion-icon>\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 <div class=\"logo-container\">\n <val-image [props]=\"resolvedLogo\" />\n </div>\n <val-form [props]=\"resetPasswordFormProps()\" (onSubmit)=\"resetPasswordHandler($event)\" />\n <div class=\"resend-link\">\n <ion-text color=\"dark\">\n {{ t('noCodeReceived') }} @if (resetResendCooldown > 0) {\n <span class=\"cooldown\">{{ t('resendIn', { seconds: resetResendCooldown.toString() }) }}</span>\n } @else {\n <a (click)=\"resendResetCode()\">{{ t('resend') }}</a>\n }\n </ion-text>\n </div>\n </section>\n </ion-content>\n </ng-template>\n</ion-modal>\n\n<!-- MFA Verify Modal -->\n<ion-modal [isOpen]=\"isMFAVerifyModalOpen\" [backdropDismiss]=\"false\" (didDismiss)=\"onMFAVerifyDismissed()\">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-buttons slot=\"end\">\n <ion-button fill=\"clear\" [attr.aria-label]=\"t('close')\" (click)=\"closeMFAVerifyModal()\">\n <ion-icon name=\"close-outline\" aria-hidden=\"true\"></ion-icon>\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 <div class=\"logo-container\">\n <val-image [props]=\"resolvedLogo\" />\n </div>\n <val-form [props]=\"mfaVerifyFormProps()\" (onSubmit)=\"verifyMFAHandler($event)\" />\n </section>\n </ion-content>\n </ng-template>\n</ion-modal>\n", styles: [".val-login{width:100%}.val-login--card{padding:1rem .75rem;background:var(--ion-color-light);border-radius:16px;box-shadow:0 2px 8px #00000014}.logo-container{max-width:130px;margin-bottom:1.5rem}.auth-link{text-align:center;margin-top:1rem;font-size:.9rem}.auth-link a{color:var(--ion-color-primary);text-decoration:none;font-weight:500;cursor:pointer}.auth-link a:hover{text-decoration:underline}.oauth-separator{display:flex;align-items:center;margin:1.5rem 0}.oauth-separator:before,.oauth-separator:after{content:\"\";flex:1;height:1px;background:var(--ion-color-medium-tint)}.oauth-separator span{padding:0 1rem;color:var(--ion-color-dark);font-size:.85rem}.oauth-buttons{margin-bottom:1rem}.oauth-buttons ion-button{--border-radius: 8px;--border-width: 1px}.oauth-buttons ion-icon{font-size:1.2rem;margin-right:.5rem}.legal-notice{margin-top:1.5rem;padding-top:1rem;border-top:1px solid var(--ion-color-medium-tint)}.legal-notice p{font-size:.75rem;line-height:1.5;text-align:center;margin:0}.legal-notice a{color:var(--ion-color-primary);text-decoration:none}.legal-notice a:hover{text-decoration:underline}.resend-link{text-align:center;margin-top:1rem;font-size:.9rem}.resend-link a{color:var(--ion-color-primary);cursor:pointer;font-weight:500}.resend-link a:hover{text-decoration:underline}.resend-link .cooldown{color:var(--ion-color-medium)}\n"] }]
|
|
40950
40979
|
}], propDecorators: { props: [{
|
|
40951
40980
|
type: Input
|
|
40952
40981
|
}], onSuccess: [{
|
|
@@ -46678,6 +46707,16 @@ const DEFAULT_NAMESPACE$h = 'CreateOrgModal';
|
|
|
46678
46707
|
* `CreateOrgModal`.
|
|
46679
46708
|
*/
|
|
46680
46709
|
class CreateOrgModalComponent {
|
|
46710
|
+
set isOpen(value) {
|
|
46711
|
+
const opening = value && !this._isOpen;
|
|
46712
|
+
this._isOpen = value;
|
|
46713
|
+
if (opening) {
|
|
46714
|
+
this._busy.set(false);
|
|
46715
|
+
}
|
|
46716
|
+
}
|
|
46717
|
+
get isOpen() {
|
|
46718
|
+
return this._isOpen;
|
|
46719
|
+
}
|
|
46681
46720
|
constructor() {
|
|
46682
46721
|
this.i18n = inject(I18nService);
|
|
46683
46722
|
this.orgService = inject(OrgService);
|
|
@@ -46686,6 +46725,9 @@ class CreateOrgModalComponent {
|
|
|
46686
46725
|
this.errors = inject(ValtechErrorService);
|
|
46687
46726
|
/** Namespace i18n con que la vista resuelve sus textos. */
|
|
46688
46727
|
this.i18nNamespace = DEFAULT_NAMESPACE$h;
|
|
46728
|
+
this._isOpen = false;
|
|
46729
|
+
this.dismissed = new EventEmitter();
|
|
46730
|
+
this.created = new EventEmitter();
|
|
46689
46731
|
this._busy = signal(false);
|
|
46690
46732
|
if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$h)) {
|
|
46691
46733
|
this.i18n.registerContent(DEFAULT_NAMESPACE$h, CREATE_ORG_MODAL_I18N);
|
|
@@ -46731,8 +46773,8 @@ class CreateOrgModalComponent {
|
|
|
46731
46773
|
duration: 3500,
|
|
46732
46774
|
});
|
|
46733
46775
|
await this.orgSwitch.switchTo(newOrg.id);
|
|
46734
|
-
this.
|
|
46735
|
-
this.
|
|
46776
|
+
this.created.emit(newOrg);
|
|
46777
|
+
this.dismissed.emit();
|
|
46736
46778
|
}
|
|
46737
46779
|
catch (err) {
|
|
46738
46780
|
this.errors.handle(err, { i18nNamespace: this.i18nNamespace });
|
|
@@ -46742,7 +46784,10 @@ class CreateOrgModalComponent {
|
|
|
46742
46784
|
}
|
|
46743
46785
|
}
|
|
46744
46786
|
dismiss() {
|
|
46745
|
-
this.
|
|
46787
|
+
this.dismissed.emit();
|
|
46788
|
+
}
|
|
46789
|
+
close() {
|
|
46790
|
+
this.dismissed.emit();
|
|
46746
46791
|
}
|
|
46747
46792
|
buildFormMeta() {
|
|
46748
46793
|
const nameField = {
|
|
@@ -46822,71 +46867,81 @@ class CreateOrgModalComponent {
|
|
|
46822
46867
|
return this.i18n.t(key, this.i18nNamespace);
|
|
46823
46868
|
}
|
|
46824
46869
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: CreateOrgModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
46825
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: CreateOrgModalComponent, isStandalone: true, selector: "val-create-org-modal", inputs: {
|
|
46826
|
-
<ion-
|
|
46827
|
-
<
|
|
46828
|
-
<ion-
|
|
46829
|
-
<ion-
|
|
46830
|
-
<
|
|
46831
|
-
|
|
46832
|
-
|
|
46833
|
-
|
|
46834
|
-
|
|
46835
|
-
|
|
46836
|
-
|
|
46837
|
-
|
|
46838
|
-
|
|
46839
|
-
|
|
46840
|
-
|
|
46841
|
-
|
|
46842
|
-
|
|
46843
|
-
|
|
46844
|
-
|
|
46845
|
-
|
|
46846
|
-
|
|
46847
|
-
|
|
46848
|
-
|
|
46849
|
-
|
|
46850
|
-
|
|
46851
|
-
|
|
46852
|
-
|
|
46870
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: CreateOrgModalComponent, isStandalone: true, selector: "val-create-org-modal", inputs: { i18nNamespace: "i18nNamespace", isOpen: "isOpen" }, outputs: { dismissed: "dismissed", created: "created" }, ngImport: i0, template: `
|
|
46871
|
+
<ion-modal [isOpen]="isOpen" (didDismiss)="close()" cssClass="val-modal-adaptive">
|
|
46872
|
+
<ng-template>
|
|
46873
|
+
<ion-header>
|
|
46874
|
+
<ion-toolbar>
|
|
46875
|
+
<ion-buttons slot="end">
|
|
46876
|
+
<ion-button fill="clear" color="dark" shape="round" (click)="dismiss()">
|
|
46877
|
+
<strong>{{ t('close') }}</strong>
|
|
46878
|
+
</ion-button>
|
|
46879
|
+
</ion-buttons>
|
|
46880
|
+
</ion-toolbar>
|
|
46881
|
+
</ion-header>
|
|
46882
|
+
<ion-content class="ion-padding">
|
|
46883
|
+
<val-display [props]="{ content: t('pageTitle'), size: 'small', color: 'dark' }" />
|
|
46884
|
+
<val-title
|
|
46885
|
+
[props]="{
|
|
46886
|
+
content: t('pageDescription'),
|
|
46887
|
+
size: 'large',
|
|
46888
|
+
color: '',
|
|
46889
|
+
bold: false,
|
|
46890
|
+
}"
|
|
46891
|
+
/>
|
|
46892
|
+
<val-form [props]="formMeta()" (onSubmit)="onSubmit($event)" />
|
|
46893
|
+
<div class="modal-close-bottom">
|
|
46894
|
+
<ion-button expand="block" fill="outline" color="dark" shape="round" (click)="dismiss()">
|
|
46895
|
+
{{ t('close') }}
|
|
46896
|
+
</ion-button>
|
|
46897
|
+
</div>
|
|
46898
|
+
</ion-content>
|
|
46899
|
+
</ng-template>
|
|
46900
|
+
</ion-modal>
|
|
46901
|
+
`, isInline: true, styles: [".modal-close-bottom{margin-top:16px}\n"], dependencies: [{ kind: "component", type: IonModal, selector: "ion-modal" }, { kind: "component", type: IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "component", type: IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: FormComponent, selector: "val-form", inputs: ["props"], outputs: ["onSubmit", "onInvalid", "onSelectChange"] }, { kind: "component", type: DisplayComponent, selector: "val-display", inputs: ["props"] }, { kind: "component", type: TitleComponent, selector: "val-title", inputs: ["props"] }] }); }
|
|
46853
46902
|
}
|
|
46854
46903
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: CreateOrgModalComponent, decorators: [{
|
|
46855
46904
|
type: Component,
|
|
46856
|
-
args: [{ selector: 'val-create-org-modal', standalone: true, imports: [IonHeader, IonToolbar, IonButtons, IonButton, IonContent, FormComponent, DisplayComponent, TitleComponent], template: `
|
|
46857
|
-
<ion-
|
|
46858
|
-
<
|
|
46859
|
-
<ion-
|
|
46860
|
-
<ion-
|
|
46861
|
-
<
|
|
46862
|
-
|
|
46863
|
-
|
|
46864
|
-
|
|
46865
|
-
|
|
46866
|
-
|
|
46867
|
-
|
|
46868
|
-
|
|
46869
|
-
|
|
46870
|
-
|
|
46871
|
-
|
|
46872
|
-
|
|
46873
|
-
|
|
46874
|
-
|
|
46875
|
-
|
|
46876
|
-
|
|
46877
|
-
|
|
46878
|
-
|
|
46879
|
-
|
|
46880
|
-
|
|
46881
|
-
|
|
46882
|
-
|
|
46905
|
+
args: [{ selector: 'val-create-org-modal', standalone: true, imports: [IonModal, IonHeader, IonToolbar, IonButtons, IonButton, IonContent, FormComponent, DisplayComponent, TitleComponent], template: `
|
|
46906
|
+
<ion-modal [isOpen]="isOpen" (didDismiss)="close()" cssClass="val-modal-adaptive">
|
|
46907
|
+
<ng-template>
|
|
46908
|
+
<ion-header>
|
|
46909
|
+
<ion-toolbar>
|
|
46910
|
+
<ion-buttons slot="end">
|
|
46911
|
+
<ion-button fill="clear" color="dark" shape="round" (click)="dismiss()">
|
|
46912
|
+
<strong>{{ t('close') }}</strong>
|
|
46913
|
+
</ion-button>
|
|
46914
|
+
</ion-buttons>
|
|
46915
|
+
</ion-toolbar>
|
|
46916
|
+
</ion-header>
|
|
46917
|
+
<ion-content class="ion-padding">
|
|
46918
|
+
<val-display [props]="{ content: t('pageTitle'), size: 'small', color: 'dark' }" />
|
|
46919
|
+
<val-title
|
|
46920
|
+
[props]="{
|
|
46921
|
+
content: t('pageDescription'),
|
|
46922
|
+
size: 'large',
|
|
46923
|
+
color: '',
|
|
46924
|
+
bold: false,
|
|
46925
|
+
}"
|
|
46926
|
+
/>
|
|
46927
|
+
<val-form [props]="formMeta()" (onSubmit)="onSubmit($event)" />
|
|
46928
|
+
<div class="modal-close-bottom">
|
|
46929
|
+
<ion-button expand="block" fill="outline" color="dark" shape="round" (click)="dismiss()">
|
|
46930
|
+
{{ t('close') }}
|
|
46931
|
+
</ion-button>
|
|
46932
|
+
</div>
|
|
46933
|
+
</ion-content>
|
|
46934
|
+
</ng-template>
|
|
46935
|
+
</ion-modal>
|
|
46883
46936
|
`, styles: [".modal-close-bottom{margin-top:16px}\n"] }]
|
|
46884
|
-
}], ctorParameters: () => [], propDecorators: {
|
|
46885
|
-
type: Input
|
|
46886
|
-
}], onSuccess: [{
|
|
46937
|
+
}], ctorParameters: () => [], propDecorators: { i18nNamespace: [{
|
|
46887
46938
|
type: Input
|
|
46888
|
-
}],
|
|
46939
|
+
}], isOpen: [{
|
|
46889
46940
|
type: Input
|
|
46941
|
+
}], dismissed: [{
|
|
46942
|
+
type: Output
|
|
46943
|
+
}], created: [{
|
|
46944
|
+
type: Output
|
|
46890
46945
|
}] } });
|
|
46891
46946
|
|
|
46892
46947
|
/**
|
|
@@ -46949,6 +47004,21 @@ const DEFAULT_NAMESPACE$g = 'Settings.DeleteAccount';
|
|
|
46949
47004
|
* proveyó el namespace `Settings.DeleteAccount`.
|
|
46950
47005
|
*/
|
|
46951
47006
|
class DeleteAccountModalComponent {
|
|
47007
|
+
set isOpen(value) {
|
|
47008
|
+
const opening = value && !this._isOpen;
|
|
47009
|
+
this._isOpen = value;
|
|
47010
|
+
if (opening) {
|
|
47011
|
+
this.hasPassword.set(null);
|
|
47012
|
+
this.codeSent.set(false);
|
|
47013
|
+
this.formState.set(ComponentStates.ENABLED);
|
|
47014
|
+
firstValueFrom(this.auth.getProfile())
|
|
47015
|
+
.then(profile => this.hasPassword.set(profile.hasPassword ?? true))
|
|
47016
|
+
.catch(() => this.hasPassword.set(true));
|
|
47017
|
+
}
|
|
47018
|
+
}
|
|
47019
|
+
get isOpen() {
|
|
47020
|
+
return this._isOpen;
|
|
47021
|
+
}
|
|
46952
47022
|
constructor() {
|
|
46953
47023
|
this.auth = inject(AuthService);
|
|
46954
47024
|
this.errors = inject(ValtechErrorService);
|
|
@@ -46956,6 +47026,8 @@ class DeleteAccountModalComponent {
|
|
|
46956
47026
|
this.toast = inject(ToastService);
|
|
46957
47027
|
/** Namespace i18n con que la vista resuelve sus textos. */
|
|
46958
47028
|
this.i18nNamespace = DEFAULT_NAMESPACE$g;
|
|
47029
|
+
this._isOpen = false;
|
|
47030
|
+
this.dismissed = new EventEmitter();
|
|
46959
47031
|
this.hasPassword = signal(null);
|
|
46960
47032
|
this.codeSent = signal(false);
|
|
46961
47033
|
this.formState = signal(ComponentStates.ENABLED);
|
|
@@ -47026,11 +47098,6 @@ class DeleteAccountModalComponent {
|
|
|
47026
47098
|
this.i18n.registerContent(DEFAULT_NAMESPACE$g, DELETE_ACCOUNT_MODAL_I18N);
|
|
47027
47099
|
}
|
|
47028
47100
|
}
|
|
47029
|
-
ngOnInit() {
|
|
47030
|
-
firstValueFrom(this.auth.getProfile())
|
|
47031
|
-
.then(profile => this.hasPassword.set(profile.hasPassword ?? true))
|
|
47032
|
-
.catch(() => this.hasPassword.set(true));
|
|
47033
|
-
}
|
|
47034
47101
|
submitBtn(token, textKey) {
|
|
47035
47102
|
return {
|
|
47036
47103
|
text: this.t(textKey),
|
|
@@ -47045,7 +47112,7 @@ class DeleteAccountModalComponent {
|
|
|
47045
47112
|
};
|
|
47046
47113
|
}
|
|
47047
47114
|
close() {
|
|
47048
|
-
this.
|
|
47115
|
+
this.dismissed.emit();
|
|
47049
47116
|
}
|
|
47050
47117
|
async onPasswordSubmit(event) {
|
|
47051
47118
|
const password = event.fields['password'];
|
|
@@ -47105,89 +47172,99 @@ class DeleteAccountModalComponent {
|
|
|
47105
47172
|
}
|
|
47106
47173
|
}
|
|
47107
47174
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DeleteAccountModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
47108
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: DeleteAccountModalComponent, isStandalone: true, selector: "val-delete-account-modal", inputs: {
|
|
47109
|
-
<ion-
|
|
47110
|
-
<
|
|
47111
|
-
<ion-
|
|
47112
|
-
<ion-
|
|
47113
|
-
<
|
|
47114
|
-
|
|
47115
|
-
|
|
47116
|
-
|
|
47117
|
-
|
|
47175
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: DeleteAccountModalComponent, isStandalone: true, selector: "val-delete-account-modal", inputs: { i18nNamespace: "i18nNamespace", isOpen: "isOpen" }, outputs: { dismissed: "dismissed" }, ngImport: i0, template: `
|
|
47176
|
+
<ion-modal [isOpen]="isOpen" (didDismiss)="close()" cssClass="val-modal-adaptive">
|
|
47177
|
+
<ng-template>
|
|
47178
|
+
<ion-header>
|
|
47179
|
+
<ion-toolbar>
|
|
47180
|
+
<ion-buttons slot="end">
|
|
47181
|
+
<ion-button fill="clear" color="dark" shape="round" (click)="close()">
|
|
47182
|
+
<strong>{{ t('close') }}</strong>
|
|
47183
|
+
</ion-button>
|
|
47184
|
+
</ion-buttons>
|
|
47185
|
+
</ion-toolbar>
|
|
47186
|
+
</ion-header>
|
|
47118
47187
|
|
|
47119
|
-
|
|
47120
|
-
|
|
47121
|
-
|
|
47122
|
-
|
|
47123
|
-
|
|
47124
|
-
|
|
47125
|
-
|
|
47126
|
-
|
|
47127
|
-
|
|
47128
|
-
|
|
47129
|
-
|
|
47130
|
-
|
|
47131
|
-
|
|
47132
|
-
|
|
47133
|
-
|
|
47134
|
-
|
|
47135
|
-
|
|
47136
|
-
|
|
47137
|
-
|
|
47138
|
-
|
|
47139
|
-
|
|
47140
|
-
|
|
47141
|
-
|
|
47142
|
-
|
|
47143
|
-
|
|
47144
|
-
|
|
47145
|
-
|
|
47188
|
+
<ion-content class="ion-padding">
|
|
47189
|
+
<section class="modal-form-section">
|
|
47190
|
+
@if (hasPassword() === null) {
|
|
47191
|
+
<div class="spinner-center">
|
|
47192
|
+
<ion-spinner name="crescent" />
|
|
47193
|
+
</div>
|
|
47194
|
+
} @else if (hasPassword()) {
|
|
47195
|
+
<val-form [props]="passwordFormProps()" (onSubmit)="onPasswordSubmit($event)" />
|
|
47196
|
+
} @else if (!codeSent()) {
|
|
47197
|
+
<val-form [props]="sendCodeFormProps()" (onSubmit)="onSendCode()" />
|
|
47198
|
+
} @else {
|
|
47199
|
+
<val-form [props]="codeFormProps()" (onSubmit)="onCodeSubmit($event)" />
|
|
47200
|
+
<div class="resend-row">
|
|
47201
|
+
<ion-button
|
|
47202
|
+
fill="clear"
|
|
47203
|
+
color="medium"
|
|
47204
|
+
size="small"
|
|
47205
|
+
[disabled]="formState() === 'WORKING'"
|
|
47206
|
+
(click)="onResendCode()"
|
|
47207
|
+
>
|
|
47208
|
+
{{ t('resend') }}
|
|
47209
|
+
</ion-button>
|
|
47210
|
+
</div>
|
|
47211
|
+
}
|
|
47212
|
+
</section>
|
|
47213
|
+
</ion-content>
|
|
47214
|
+
</ng-template>
|
|
47215
|
+
</ion-modal>
|
|
47216
|
+
`, isInline: true, styles: [".modal-form-section{display:flex;flex-direction:column;gap:16px;max-width:420px;margin:0 auto;padding-top:8px}.resend-row{display:flex;justify-content:center;margin-top:-8px}.spinner-center{display:flex;justify-content:center;padding-top:32px}\n"], dependencies: [{ kind: "component", type: IonModal, selector: "ion-modal" }, { kind: "component", type: IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "component", type: IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { kind: "component", type: FormComponent, selector: "val-form", inputs: ["props"], outputs: ["onSubmit", "onInvalid", "onSelectChange"] }] }); }
|
|
47146
47217
|
}
|
|
47147
47218
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: DeleteAccountModalComponent, decorators: [{
|
|
47148
47219
|
type: Component,
|
|
47149
|
-
args: [{ selector: 'val-delete-account-modal', standalone: true, imports: [IonHeader, IonToolbar, IonButtons, IonButton, IonContent, IonSpinner, FormComponent], template: `
|
|
47150
|
-
<ion-
|
|
47151
|
-
<
|
|
47152
|
-
<ion-
|
|
47153
|
-
<ion-
|
|
47154
|
-
<
|
|
47155
|
-
|
|
47156
|
-
|
|
47157
|
-
|
|
47158
|
-
|
|
47220
|
+
args: [{ selector: 'val-delete-account-modal', standalone: true, imports: [IonModal, IonHeader, IonToolbar, IonButtons, IonButton, IonContent, IonSpinner, FormComponent], template: `
|
|
47221
|
+
<ion-modal [isOpen]="isOpen" (didDismiss)="close()" cssClass="val-modal-adaptive">
|
|
47222
|
+
<ng-template>
|
|
47223
|
+
<ion-header>
|
|
47224
|
+
<ion-toolbar>
|
|
47225
|
+
<ion-buttons slot="end">
|
|
47226
|
+
<ion-button fill="clear" color="dark" shape="round" (click)="close()">
|
|
47227
|
+
<strong>{{ t('close') }}</strong>
|
|
47228
|
+
</ion-button>
|
|
47229
|
+
</ion-buttons>
|
|
47230
|
+
</ion-toolbar>
|
|
47231
|
+
</ion-header>
|
|
47159
47232
|
|
|
47160
|
-
|
|
47161
|
-
|
|
47162
|
-
|
|
47163
|
-
|
|
47164
|
-
|
|
47165
|
-
|
|
47166
|
-
|
|
47167
|
-
|
|
47168
|
-
|
|
47169
|
-
|
|
47170
|
-
|
|
47171
|
-
|
|
47172
|
-
|
|
47173
|
-
|
|
47174
|
-
|
|
47175
|
-
|
|
47176
|
-
|
|
47177
|
-
|
|
47178
|
-
|
|
47179
|
-
|
|
47180
|
-
|
|
47181
|
-
|
|
47182
|
-
|
|
47183
|
-
|
|
47184
|
-
|
|
47185
|
-
|
|
47233
|
+
<ion-content class="ion-padding">
|
|
47234
|
+
<section class="modal-form-section">
|
|
47235
|
+
@if (hasPassword() === null) {
|
|
47236
|
+
<div class="spinner-center">
|
|
47237
|
+
<ion-spinner name="crescent" />
|
|
47238
|
+
</div>
|
|
47239
|
+
} @else if (hasPassword()) {
|
|
47240
|
+
<val-form [props]="passwordFormProps()" (onSubmit)="onPasswordSubmit($event)" />
|
|
47241
|
+
} @else if (!codeSent()) {
|
|
47242
|
+
<val-form [props]="sendCodeFormProps()" (onSubmit)="onSendCode()" />
|
|
47243
|
+
} @else {
|
|
47244
|
+
<val-form [props]="codeFormProps()" (onSubmit)="onCodeSubmit($event)" />
|
|
47245
|
+
<div class="resend-row">
|
|
47246
|
+
<ion-button
|
|
47247
|
+
fill="clear"
|
|
47248
|
+
color="medium"
|
|
47249
|
+
size="small"
|
|
47250
|
+
[disabled]="formState() === 'WORKING'"
|
|
47251
|
+
(click)="onResendCode()"
|
|
47252
|
+
>
|
|
47253
|
+
{{ t('resend') }}
|
|
47254
|
+
</ion-button>
|
|
47255
|
+
</div>
|
|
47256
|
+
}
|
|
47257
|
+
</section>
|
|
47258
|
+
</ion-content>
|
|
47259
|
+
</ng-template>
|
|
47260
|
+
</ion-modal>
|
|
47186
47261
|
`, styles: [".modal-form-section{display:flex;flex-direction:column;gap:16px;max-width:420px;margin:0 auto;padding-top:8px}.resend-row{display:flex;justify-content:center;margin-top:-8px}.spinner-center{display:flex;justify-content:center;padding-top:32px}\n"] }]
|
|
47187
|
-
}], ctorParameters: () => [], propDecorators: {
|
|
47262
|
+
}], ctorParameters: () => [], propDecorators: { i18nNamespace: [{
|
|
47188
47263
|
type: Input
|
|
47189
|
-
}],
|
|
47264
|
+
}], isOpen: [{
|
|
47190
47265
|
type: Input
|
|
47266
|
+
}], dismissed: [{
|
|
47267
|
+
type: Output
|
|
47191
47268
|
}] } });
|
|
47192
47269
|
|
|
47193
47270
|
/**
|
|
@@ -47878,6 +47955,8 @@ class AccountViewComponent {
|
|
|
47878
47955
|
retrying: this.orgsLoading(),
|
|
47879
47956
|
});
|
|
47880
47957
|
});
|
|
47958
|
+
this.createOrgOpen = signal(false);
|
|
47959
|
+
this.deleteAccountOpen = signal(false);
|
|
47881
47960
|
this.pendingInvites = signal([]);
|
|
47882
47961
|
this.pendingInvitesLoading = signal(false);
|
|
47883
47962
|
this.inviteAccepting = signal(null);
|
|
@@ -47991,40 +48070,18 @@ class AccountViewComponent {
|
|
|
47991
48070
|
});
|
|
47992
48071
|
}
|
|
47993
48072
|
onDeleteAccount() {
|
|
47994
|
-
|
|
47995
|
-
component: DeleteAccountModalComponent,
|
|
47996
|
-
componentProps: {
|
|
47997
|
-
i18nNamespace: 'Settings.DeleteAccount',
|
|
47998
|
-
},
|
|
47999
|
-
breakpoints: {
|
|
48000
|
-
breakpoints: [0, 0.85],
|
|
48001
|
-
initialBreakpoint: 0.85,
|
|
48002
|
-
showHandle: true,
|
|
48003
|
-
},
|
|
48004
|
-
backdropDismiss: false,
|
|
48005
|
-
});
|
|
48073
|
+
this.deleteAccountOpen.set(true);
|
|
48006
48074
|
this.resolvedConfig().onAccountDeleted?.();
|
|
48007
48075
|
}
|
|
48008
48076
|
onManageOrg() {
|
|
48009
48077
|
this.nav.navigateByUrl(this.resolvedConfig().manageOrgRoute);
|
|
48010
48078
|
}
|
|
48011
48079
|
onNewOrg() {
|
|
48012
|
-
|
|
48013
|
-
|
|
48014
|
-
|
|
48015
|
-
|
|
48016
|
-
|
|
48017
|
-
this.loadOrgs();
|
|
48018
|
-
this.resolvedConfig().onOrgCreated?.(newOrg);
|
|
48019
|
-
},
|
|
48020
|
-
},
|
|
48021
|
-
breakpoints: {
|
|
48022
|
-
breakpoints: [0, 0.92],
|
|
48023
|
-
initialBreakpoint: 0.92,
|
|
48024
|
-
showHandle: true,
|
|
48025
|
-
},
|
|
48026
|
-
backdropDismiss: true,
|
|
48027
|
-
});
|
|
48080
|
+
this.createOrgOpen.set(true);
|
|
48081
|
+
}
|
|
48082
|
+
onOrgCreated(newOrg) {
|
|
48083
|
+
this.loadOrgs();
|
|
48084
|
+
this.resolvedConfig().onOrgCreated?.(newOrg);
|
|
48028
48085
|
}
|
|
48029
48086
|
onMoreInfo() {
|
|
48030
48087
|
void this.modalService.open({
|
|
@@ -48314,8 +48371,19 @@ class AccountViewComponent {
|
|
|
48314
48371
|
</div>
|
|
48315
48372
|
</section>
|
|
48316
48373
|
}
|
|
48374
|
+
<val-create-org-modal
|
|
48375
|
+
[isOpen]="createOrgOpen()"
|
|
48376
|
+
[i18nNamespace]="'CreateOrgModal'"
|
|
48377
|
+
(dismissed)="createOrgOpen.set(false)"
|
|
48378
|
+
(created)="onOrgCreated($event)"
|
|
48379
|
+
/>
|
|
48380
|
+
<val-delete-account-modal
|
|
48381
|
+
[isOpen]="deleteAccountOpen()"
|
|
48382
|
+
[i18nNamespace]="'Settings.DeleteAccount'"
|
|
48383
|
+
(dismissed)="deleteAccountOpen.set(false)"
|
|
48384
|
+
/>
|
|
48317
48385
|
</div>
|
|
48318
|
-
`, isInline: true, styles: [".page{padding:16px 0;max-width:720px;margin:0 auto}.page-header{margin-bottom:16px}.settings-section{padding:16px 0}.settings-section+.settings-section{border-top:1px solid var(--val-border-color, rgba(0, 0, 0, .08))}.settings-section val-title{display:block;margin-bottom:12px}.section-body{display:flex;flex-direction:column;gap:10px}.row-actions{margin-top:12px}.row-actions--gap{display:flex;flex-wrap:wrap;gap:8px}.section-header-row{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:8px}.org-new-cta-card{display:block;margin-top:12px}.orgs-empty-card{background:var(--ion-color-secondary-tint, rgba(var(--ion-color-secondary-rgb, 130, 101, 208), .2));border-radius:18px;padding:18px 16px 12px;display:flex;flex-direction:column;gap:8px}.orgs-empty-card__main{display:flex;align-items:center;gap:10px}.orgs-empty-card__icon{font-size:20px;flex-shrink:0;color:var(--ion-color-dark)}.orgs-empty-card__text{font-size:.875rem;color:var(--ion-color-dark)}.orgs-empty-card__link{background:none;border:none;padding:0;cursor:pointer;font-size:.8125rem;color:var(--ion-color-primary);text-align:left;text-decoration:underline;text-underline-offset:2px;font-family:inherit}.orgs-list{display:flex;flex-direction:column;gap:8px}.org-card{display:flex;align-items:center;gap:14px;padding:14px 16px;border-radius:12px;background:var(--ion-color-light, #f4f5f8);border:1.5px solid transparent;cursor:pointer;transition:background .15s,border-color .15s}.org-card:active{opacity:.75}.org-card--active{border-color:var(--ion-color-primary);background:color-mix(in srgb,var(--ion-color-primary) 8%,transparent)}:host-context(body.dark) .org-card,:host-context(html.ion-palette-dark) .org-card,:host-context([data-theme=\"dark\"]) .org-card{background:#ffffff0d}:host-context(body.dark) .org-card--active,:host-context(html.ion-palette-dark) .org-card--active,:host-context([data-theme=\"dark\"]) .org-card--active{background:color-mix(in srgb,var(--ion-color-primary) 15%,transparent)}.org-card__icon{width:40px;height:40px;border-radius:50%;background:color-mix(in srgb,var(--ion-color-primary) 15%,transparent);display:flex;align-items:center;justify-content:center;flex-shrink:0;font-size:20px;color:var(--ion-color-primary)}.org-card__body{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}.org-card__name{font-weight:600;font-size:.95rem;color:var(--ion-color-dark);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.org-card__meta{display:flex;flex-direction:column;gap:1px;margin-top:3px}.org-card__meta-item{font-size:.78rem;color:var(--ion-color-dark)}.org-card__meta-label{font-weight:600;color:var(--ion-color-dark);margin-right:4px}.org-card__end{display:flex;align-items:center;gap:8px;flex-shrink:0}\n"], dependencies: [{ kind: "component", type: DisplayComponent, selector: "val-display", inputs: ["props"] }, { kind: "component", type: EmptyStateComponent, selector: "val-empty-state", inputs: ["props"] }, { kind: "component", type: SkeletonLayoutComponent, selector: "val-skeleton-layout", inputs: ["props"] }, { kind: "component", type: TitleComponent, selector: "val-title", inputs: ["props"] }, { kind: "component", type: TextComponent, selector: "val-text", inputs: ["props"] }, { kind: "component", type: ButtonComponent, selector: "val-button", inputs: ["preset", "props"], outputs: ["onClick"] }, { kind: "component", type: CtaCardComponent, selector: "val-cta-card", inputs: ["props"], outputs: ["onAction"] }, { kind: "component", type: InvitationCardComponent, selector: "val-invitation-card", inputs: ["props"], outputs: ["onAccept", "onDecline"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "pipe", type: TitleCasePipe, name: "titlecase" }] }); }
|
|
48386
|
+
`, isInline: true, styles: [".page{padding:16px 0;max-width:720px;margin:0 auto}.page-header{margin-bottom:16px}.settings-section{padding:16px 0}.settings-section+.settings-section{border-top:1px solid var(--val-border-color, rgba(0, 0, 0, .08))}.settings-section val-title{display:block;margin-bottom:12px}.section-body{display:flex;flex-direction:column;gap:10px}.row-actions{margin-top:12px}.row-actions--gap{display:flex;flex-wrap:wrap;gap:8px}.section-header-row{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:8px}.org-new-cta-card{display:block;margin-top:12px}.orgs-empty-card{background:var(--ion-color-secondary-tint, rgba(var(--ion-color-secondary-rgb, 130, 101, 208), .2));border-radius:18px;padding:18px 16px 12px;display:flex;flex-direction:column;gap:8px}.orgs-empty-card__main{display:flex;align-items:center;gap:10px}.orgs-empty-card__icon{font-size:20px;flex-shrink:0;color:var(--ion-color-dark)}.orgs-empty-card__text{font-size:.875rem;color:var(--ion-color-dark)}.orgs-empty-card__link{background:none;border:none;padding:0;cursor:pointer;font-size:.8125rem;color:var(--ion-color-primary);text-align:left;text-decoration:underline;text-underline-offset:2px;font-family:inherit}.orgs-list{display:flex;flex-direction:column;gap:8px}.org-card{display:flex;align-items:center;gap:14px;padding:14px 16px;border-radius:12px;background:var(--ion-color-light, #f4f5f8);border:1.5px solid transparent;cursor:pointer;transition:background .15s,border-color .15s}.org-card:active{opacity:.75}.org-card--active{border-color:var(--ion-color-primary);background:color-mix(in srgb,var(--ion-color-primary) 8%,transparent)}:host-context(body.dark) .org-card,:host-context(html.ion-palette-dark) .org-card,:host-context([data-theme=\"dark\"]) .org-card{background:#ffffff0d}:host-context(body.dark) .org-card--active,:host-context(html.ion-palette-dark) .org-card--active,:host-context([data-theme=\"dark\"]) .org-card--active{background:color-mix(in srgb,var(--ion-color-primary) 15%,transparent)}.org-card__icon{width:40px;height:40px;border-radius:50%;background:color-mix(in srgb,var(--ion-color-primary) 15%,transparent);display:flex;align-items:center;justify-content:center;flex-shrink:0;font-size:20px;color:var(--ion-color-primary)}.org-card__body{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}.org-card__name{font-weight:600;font-size:.95rem;color:var(--ion-color-dark);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.org-card__meta{display:flex;flex-direction:column;gap:1px;margin-top:3px}.org-card__meta-item{font-size:.78rem;color:var(--ion-color-dark)}.org-card__meta-label{font-weight:600;color:var(--ion-color-dark);margin-right:4px}.org-card__end{display:flex;align-items:center;gap:8px;flex-shrink:0}\n"], dependencies: [{ kind: "component", type: DisplayComponent, selector: "val-display", inputs: ["props"] }, { kind: "component", type: EmptyStateComponent, selector: "val-empty-state", inputs: ["props"] }, { kind: "component", type: SkeletonLayoutComponent, selector: "val-skeleton-layout", inputs: ["props"] }, { kind: "component", type: TitleComponent, selector: "val-title", inputs: ["props"] }, { kind: "component", type: TextComponent, selector: "val-text", inputs: ["props"] }, { kind: "component", type: ButtonComponent, selector: "val-button", inputs: ["preset", "props"], outputs: ["onClick"] }, { kind: "component", type: CtaCardComponent, selector: "val-cta-card", inputs: ["props"], outputs: ["onAction"] }, { kind: "component", type: InvitationCardComponent, selector: "val-invitation-card", inputs: ["props"], outputs: ["onAccept", "onDecline"] }, { kind: "component", type: CreateOrgModalComponent, selector: "val-create-org-modal", inputs: ["i18nNamespace", "isOpen"], outputs: ["dismissed", "created"] }, { kind: "component", type: DeleteAccountModalComponent, selector: "val-delete-account-modal", inputs: ["i18nNamespace", "isOpen"], outputs: ["dismissed"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "pipe", type: TitleCasePipe, name: "titlecase" }] }); }
|
|
48319
48387
|
}
|
|
48320
48388
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AccountViewComponent, decorators: [{
|
|
48321
48389
|
type: Component,
|
|
@@ -48328,6 +48396,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
48328
48396
|
ButtonComponent,
|
|
48329
48397
|
CtaCardComponent,
|
|
48330
48398
|
InvitationCardComponent,
|
|
48399
|
+
CreateOrgModalComponent,
|
|
48400
|
+
DeleteAccountModalComponent,
|
|
48331
48401
|
IonIcon,
|
|
48332
48402
|
IonButton,
|
|
48333
48403
|
TitleCasePipe,
|
|
@@ -48501,6 +48571,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
48501
48571
|
</div>
|
|
48502
48572
|
</section>
|
|
48503
48573
|
}
|
|
48574
|
+
<val-create-org-modal
|
|
48575
|
+
[isOpen]="createOrgOpen()"
|
|
48576
|
+
[i18nNamespace]="'CreateOrgModal'"
|
|
48577
|
+
(dismissed)="createOrgOpen.set(false)"
|
|
48578
|
+
(created)="onOrgCreated($event)"
|
|
48579
|
+
/>
|
|
48580
|
+
<val-delete-account-modal
|
|
48581
|
+
[isOpen]="deleteAccountOpen()"
|
|
48582
|
+
[i18nNamespace]="'Settings.DeleteAccount'"
|
|
48583
|
+
(dismissed)="deleteAccountOpen.set(false)"
|
|
48584
|
+
/>
|
|
48504
48585
|
</div>
|
|
48505
48586
|
`, styles: [".page{padding:16px 0;max-width:720px;margin:0 auto}.page-header{margin-bottom:16px}.settings-section{padding:16px 0}.settings-section+.settings-section{border-top:1px solid var(--val-border-color, rgba(0, 0, 0, .08))}.settings-section val-title{display:block;margin-bottom:12px}.section-body{display:flex;flex-direction:column;gap:10px}.row-actions{margin-top:12px}.row-actions--gap{display:flex;flex-wrap:wrap;gap:8px}.section-header-row{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:8px}.org-new-cta-card{display:block;margin-top:12px}.orgs-empty-card{background:var(--ion-color-secondary-tint, rgba(var(--ion-color-secondary-rgb, 130, 101, 208), .2));border-radius:18px;padding:18px 16px 12px;display:flex;flex-direction:column;gap:8px}.orgs-empty-card__main{display:flex;align-items:center;gap:10px}.orgs-empty-card__icon{font-size:20px;flex-shrink:0;color:var(--ion-color-dark)}.orgs-empty-card__text{font-size:.875rem;color:var(--ion-color-dark)}.orgs-empty-card__link{background:none;border:none;padding:0;cursor:pointer;font-size:.8125rem;color:var(--ion-color-primary);text-align:left;text-decoration:underline;text-underline-offset:2px;font-family:inherit}.orgs-list{display:flex;flex-direction:column;gap:8px}.org-card{display:flex;align-items:center;gap:14px;padding:14px 16px;border-radius:12px;background:var(--ion-color-light, #f4f5f8);border:1.5px solid transparent;cursor:pointer;transition:background .15s,border-color .15s}.org-card:active{opacity:.75}.org-card--active{border-color:var(--ion-color-primary);background:color-mix(in srgb,var(--ion-color-primary) 8%,transparent)}:host-context(body.dark) .org-card,:host-context(html.ion-palette-dark) .org-card,:host-context([data-theme=\"dark\"]) .org-card{background:#ffffff0d}:host-context(body.dark) .org-card--active,:host-context(html.ion-palette-dark) .org-card--active,:host-context([data-theme=\"dark\"]) .org-card--active{background:color-mix(in srgb,var(--ion-color-primary) 15%,transparent)}.org-card__icon{width:40px;height:40px;border-radius:50%;background:color-mix(in srgb,var(--ion-color-primary) 15%,transparent);display:flex;align-items:center;justify-content:center;flex-shrink:0;font-size:20px;color:var(--ion-color-primary)}.org-card__body{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}.org-card__name{font-weight:600;font-size:.95rem;color:var(--ion-color-dark);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.org-card__meta{display:flex;flex-direction:column;gap:1px;margin-top:3px}.org-card__meta-item{font-size:.78rem;color:var(--ion-color-dark)}.org-card__meta-label{font-weight:600;color:var(--ion-color-dark);margin-right:4px}.org-card__end{display:flex;align-items:center;gap:8px;flex-shrink:0}\n"] }]
|
|
48506
48587
|
}], ctorParameters: () => [], propDecorators: { config: [{
|
|
@@ -50004,188 +50085,880 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
50004
50085
|
}] } });
|
|
50005
50086
|
|
|
50006
50087
|
/**
|
|
50007
|
-
* Defaults i18n (es/en) embebidos en `val-
|
|
50008
|
-
*
|
|
50009
|
-
*
|
|
50010
|
-
*
|
|
50088
|
+
* Defaults i18n (es/en) embebidos en `val-permissions-view`. Auto-registrados en
|
|
50089
|
+
* el constructor del componente si el consumer no proveyó el namespace
|
|
50090
|
+
* (`Settings.Permissions` por default). SOLO incluye las keys que la vista usa.
|
|
50091
|
+
*
|
|
50092
|
+
* Portado EXACTO de `PERMISSIONS_I18N` (showcase) bajo el proceso de ADR-021.
|
|
50011
50093
|
*/
|
|
50012
|
-
const
|
|
50094
|
+
const PERMISSIONS_VIEW_I18N = {
|
|
50013
50095
|
es: {
|
|
50014
|
-
|
|
50015
|
-
|
|
50016
|
-
|
|
50017
|
-
|
|
50018
|
-
|
|
50019
|
-
|
|
50020
|
-
|
|
50096
|
+
pageTitle: 'Permisos',
|
|
50097
|
+
pageDescription: 'Roles y permisos de esta app en la organización activa',
|
|
50098
|
+
systemRolesTitle: 'Roles del sistema',
|
|
50099
|
+
systemRolesHint: 'Predefinidos por la plataforma. No editables.',
|
|
50100
|
+
customRolesTitle: 'Roles personalizados',
|
|
50101
|
+
customRolesHint: 'Definidos por tu organización.',
|
|
50102
|
+
systemBadge: 'Sistema',
|
|
50103
|
+
noCustomRoles: 'Esta organización no tiene roles personalizados.',
|
|
50104
|
+
noPermissions: 'Sin permisos asignados.',
|
|
50105
|
+
// Grupos de alcance
|
|
50106
|
+
groupApp: 'Permisos de la app',
|
|
50107
|
+
groupOrg: 'Permisos de la organización',
|
|
50108
|
+
groupPlatform: 'Permisos de plataforma',
|
|
50109
|
+
groupOther: 'Otras apps',
|
|
50110
|
+
// Roles del sistema
|
|
50111
|
+
roleViewer: 'Visor',
|
|
50112
|
+
roleEditor: 'Editor',
|
|
50113
|
+
roleAdmin: 'Administrador',
|
|
50114
|
+
roleSuperAdmin: 'Super Administrador',
|
|
50115
|
+
roleBingoAdmin: 'Bingo · Administrador',
|
|
50116
|
+
roleBingoTesorero: 'Bingo · Tesorero',
|
|
50117
|
+
roleBingoTaquilla: 'Bingo · Taquilla',
|
|
50118
|
+
roleBingoOperator: 'Bingo · Operador',
|
|
50119
|
+
// Apps
|
|
50120
|
+
app_showcase: 'Showcase',
|
|
50121
|
+
app_bingo: 'Bingo',
|
|
50122
|
+
// Recursos
|
|
50123
|
+
permUsers: 'Usuarios',
|
|
50124
|
+
permRoles: 'Roles',
|
|
50125
|
+
permRbac: 'Control de acceso',
|
|
50126
|
+
permApps: 'Apps',
|
|
50127
|
+
'permApi-keys': 'API keys',
|
|
50128
|
+
'permEmail-logs': 'Logs de correo',
|
|
50129
|
+
'permSms-logs': 'Logs de SMS',
|
|
50130
|
+
permDocuments: 'Documentos',
|
|
50131
|
+
permMedia: 'Media',
|
|
50132
|
+
permTemplates: 'Plantillas',
|
|
50133
|
+
permRequests: 'Solicitudes',
|
|
50134
|
+
permBingo: 'Bingo',
|
|
50135
|
+
// Acciones
|
|
50136
|
+
permRead: 'Leer',
|
|
50137
|
+
permWrite: 'Escribir',
|
|
50138
|
+
permCreate: 'Crear',
|
|
50139
|
+
permDelete: 'Eliminar',
|
|
50140
|
+
permManage: 'Gestionar',
|
|
50141
|
+
permEdit: 'Editar',
|
|
50142
|
+
permSell: 'Vender',
|
|
50143
|
+
permCheckin: 'Canjear',
|
|
50144
|
+
permSettle: 'Rendir',
|
|
50145
|
+
permExport: 'Exportar',
|
|
50146
|
+
permOperate: 'Operar',
|
|
50147
|
+
permAll: 'Todo',
|
|
50148
|
+
// Loading / error
|
|
50149
|
+
errorTitle: 'Error al cargar permisos',
|
|
50150
|
+
retry: 'Reintentar',
|
|
50021
50151
|
},
|
|
50022
50152
|
en: {
|
|
50023
|
-
|
|
50024
|
-
|
|
50025
|
-
|
|
50026
|
-
|
|
50027
|
-
|
|
50028
|
-
|
|
50029
|
-
|
|
50153
|
+
pageTitle: 'Permissions',
|
|
50154
|
+
pageDescription: 'Roles and permissions of this app in the active organization',
|
|
50155
|
+
systemRolesTitle: 'System roles',
|
|
50156
|
+
systemRolesHint: 'Defined by the platform. Not editable.',
|
|
50157
|
+
customRolesTitle: 'Custom roles',
|
|
50158
|
+
customRolesHint: 'Defined by your organization.',
|
|
50159
|
+
systemBadge: 'System',
|
|
50160
|
+
noCustomRoles: 'This organization has no custom roles.',
|
|
50161
|
+
noPermissions: 'No permissions assigned.',
|
|
50162
|
+
groupApp: 'App permissions',
|
|
50163
|
+
groupOrg: 'Organization permissions',
|
|
50164
|
+
groupPlatform: 'Platform permissions',
|
|
50165
|
+
groupOther: 'Other apps',
|
|
50166
|
+
roleViewer: 'Viewer',
|
|
50167
|
+
roleEditor: 'Editor',
|
|
50168
|
+
roleAdmin: 'Administrator',
|
|
50169
|
+
roleSuperAdmin: 'Super Administrator',
|
|
50170
|
+
roleBingoAdmin: 'Bingo · Administrator',
|
|
50171
|
+
roleBingoTesorero: 'Bingo · Treasurer',
|
|
50172
|
+
roleBingoTaquilla: 'Bingo · Box office',
|
|
50173
|
+
roleBingoOperator: 'Bingo · Operator',
|
|
50174
|
+
app_showcase: 'Showcase',
|
|
50175
|
+
app_bingo: 'Bingo',
|
|
50176
|
+
permUsers: 'Users',
|
|
50177
|
+
permRoles: 'Roles',
|
|
50178
|
+
permRbac: 'Access control',
|
|
50179
|
+
permApps: 'Apps',
|
|
50180
|
+
'permApi-keys': 'API keys',
|
|
50181
|
+
'permEmail-logs': 'Email logs',
|
|
50182
|
+
'permSms-logs': 'SMS logs',
|
|
50183
|
+
permDocuments: 'Documents',
|
|
50184
|
+
permMedia: 'Media',
|
|
50185
|
+
permTemplates: 'Templates',
|
|
50186
|
+
permRequests: 'Requests',
|
|
50187
|
+
permBingo: 'Bingo',
|
|
50188
|
+
permRead: 'Read',
|
|
50189
|
+
permWrite: 'Write',
|
|
50190
|
+
permCreate: 'Create',
|
|
50191
|
+
permDelete: 'Delete',
|
|
50192
|
+
permManage: 'Manage',
|
|
50193
|
+
permEdit: 'Edit',
|
|
50194
|
+
permSell: 'Sell',
|
|
50195
|
+
permCheckin: 'Check-in',
|
|
50196
|
+
permSettle: 'Settle',
|
|
50197
|
+
permExport: 'Export',
|
|
50198
|
+
permOperate: 'Operate',
|
|
50199
|
+
permAll: 'All',
|
|
50200
|
+
errorTitle: 'Error loading permissions',
|
|
50201
|
+
retry: 'Retry',
|
|
50030
50202
|
},
|
|
50031
50203
|
};
|
|
50032
50204
|
|
|
50033
|
-
const DEFAULT_NAMESPACE$9 = 'Settings.
|
|
50205
|
+
const DEFAULT_NAMESPACE$9 = 'Settings.Permissions';
|
|
50034
50206
|
/**
|
|
50035
|
-
* `val-
|
|
50036
|
-
*
|
|
50037
|
-
*
|
|
50207
|
+
* `val-permissions-view` — vista Permisos full-feature autocontenida (organism).
|
|
50208
|
+
* Visor RBAC de solo lectura: muestra los roles del sistema (Visor/Editor/Admin +
|
|
50209
|
+
* roles de plataforma) y los roles personalizados de la org activa, con el
|
|
50210
|
+
* catálogo de permisos agrupado por alcance (app actual / plataforma / otras apps).
|
|
50211
|
+
* Promovida desde `showcase` bajo el proceso de ADR-021.
|
|
50038
50212
|
*
|
|
50039
|
-
*
|
|
50040
|
-
*
|
|
50041
|
-
*
|
|
50042
|
-
*
|
|
50213
|
+
* **RBAC interno** (como `organization-view`): lee la org activa vía
|
|
50214
|
+
* `AuthService.user().activeOrgId`. La vista NO muta nada — el control de quién
|
|
50215
|
+
* puede verla es responsabilidad del guard de ruta del consumer; no hay gating
|
|
50216
|
+
* fino interno ni se expone como config.
|
|
50043
50217
|
*
|
|
50044
|
-
*
|
|
50045
|
-
*
|
|
50046
|
-
*
|
|
50218
|
+
* Datos: `forkJoin` de `OrgService.listOrgRoles(orgId)` (roles de la org) +
|
|
50219
|
+
* `PermissionCatalogService.getCatalog(orgId)` (catálogo + appId actual). El
|
|
50220
|
+
* catálogo define el alcance (`scope`/`appId`) de cada recurso para agrupar.
|
|
50047
50221
|
*
|
|
50048
|
-
*
|
|
50049
|
-
* `
|
|
50222
|
+
* NO renderiza ion-content — vive dentro de val-page-wrapper. `ActivatedRoute` se
|
|
50223
|
+
* inyecta `{ optional: true }` solo para leer el route data de config. Auto-registra
|
|
50224
|
+
* sus defaults i18n (es/en) si el consumer no proveyó el namespace (default
|
|
50225
|
+
* `Settings.Permissions`).
|
|
50050
50226
|
*/
|
|
50051
|
-
class
|
|
50227
|
+
class PermissionsViewComponent {
|
|
50228
|
+
/** Namespace i18n resuelto (capturado para llamadas no-reactivas). */
|
|
50229
|
+
get ns() {
|
|
50230
|
+
return this.resolvedConfig().i18nNamespace;
|
|
50231
|
+
}
|
|
50052
50232
|
constructor() {
|
|
50053
50233
|
this.i18n = inject(I18nService);
|
|
50054
|
-
|
|
50055
|
-
this.
|
|
50056
|
-
|
|
50057
|
-
this.
|
|
50058
|
-
this.
|
|
50059
|
-
|
|
50060
|
-
|
|
50061
|
-
|
|
50062
|
-
|
|
50063
|
-
|
|
50064
|
-
|
|
50234
|
+
this.orgService = inject(OrgService);
|
|
50235
|
+
this.catalog = inject(PermissionCatalogService);
|
|
50236
|
+
this.auth = inject(AuthService);
|
|
50237
|
+
this.nav = inject(NavigationService);
|
|
50238
|
+
this.route = inject(ActivatedRoute, { optional: true });
|
|
50239
|
+
/**
|
|
50240
|
+
* Cuando `true`, la vista está embebida en un modal — suprime el back-header
|
|
50241
|
+
* (lo gestiona el modal padre). Default `false` (uso como ruta).
|
|
50242
|
+
*/
|
|
50243
|
+
this.isModal = false;
|
|
50244
|
+
this.resolvedConfig = computed(() => {
|
|
50245
|
+
const fromRoute = (this.route?.snapshot.data['permissionsConfig'] ?? {});
|
|
50246
|
+
const merged = { ...fromRoute, ...(this.config ?? {}) };
|
|
50247
|
+
return {
|
|
50248
|
+
showSystemRoles: merged.showSystemRoles ?? true,
|
|
50249
|
+
showCustomRoles: merged.showCustomRoles ?? true,
|
|
50250
|
+
i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$9,
|
|
50251
|
+
};
|
|
50065
50252
|
});
|
|
50066
|
-
this.
|
|
50067
|
-
|
|
50068
|
-
|
|
50253
|
+
this.loading = signal(false);
|
|
50254
|
+
this.loadError = signal(null);
|
|
50255
|
+
this.roles = signal([]);
|
|
50256
|
+
this.systemRoles = computed(() => this.roles().filter(r => r.isSystem));
|
|
50257
|
+
this.customRoles = computed(() => this.roles().filter(r => !r.isSystem));
|
|
50258
|
+
this.activeOrgId = computed(() => {
|
|
50259
|
+
const u = this.auth.user();
|
|
50260
|
+
return u?.activeOrgId ?? u?.activeOrg ?? '';
|
|
50261
|
+
});
|
|
50262
|
+
this.retryButtonProps = computed(() => ({
|
|
50263
|
+
text: this.tt('retry'),
|
|
50264
|
+
color: 'dark',
|
|
50265
|
+
fill: 'outline',
|
|
50266
|
+
size: 'default',
|
|
50267
|
+
type: 'button',
|
|
50268
|
+
state: this.loading() ? ComponentStates.DISABLED : ComponentStates.ENABLED,
|
|
50269
|
+
}));
|
|
50270
|
+
const ns = this.ns;
|
|
50271
|
+
if (!this.i18n.hasNamespace(ns)) {
|
|
50272
|
+
this.i18n.registerContent(ns, PERMISSIONS_VIEW_I18N);
|
|
50069
50273
|
}
|
|
50274
|
+
this.load();
|
|
50275
|
+
connectPageRefresh(() => this.load());
|
|
50070
50276
|
}
|
|
50071
50277
|
ngOnInit() {
|
|
50072
|
-
if (!this.
|
|
50073
|
-
this.
|
|
50278
|
+
if (!this.isModal) {
|
|
50279
|
+
this.nav.setBackHeader('pageTitle', this.ns, { withMenu: true });
|
|
50074
50280
|
}
|
|
50075
50281
|
}
|
|
50076
|
-
|
|
50077
|
-
const
|
|
50078
|
-
if (!
|
|
50282
|
+
load() {
|
|
50283
|
+
const orgId = this.activeOrgId();
|
|
50284
|
+
if (!orgId)
|
|
50079
50285
|
return;
|
|
50080
|
-
this.
|
|
50081
|
-
this.
|
|
50286
|
+
this.loading.set(true);
|
|
50287
|
+
this.loadError.set(null);
|
|
50288
|
+
forkJoin({
|
|
50289
|
+
roles: this.orgService.listOrgRoles(orgId),
|
|
50290
|
+
catalog: this.catalog.getCatalog(orgId),
|
|
50291
|
+
}).subscribe({
|
|
50292
|
+
next: ({ roles: rawRoles, catalog }) => {
|
|
50293
|
+
const byResource = new Map();
|
|
50294
|
+
for (const r of catalog.permissions)
|
|
50295
|
+
byResource.set(r.resource, r);
|
|
50296
|
+
const currentAppId = catalog.appId;
|
|
50297
|
+
// Labeler con la SSOT del backend (labels del catálogo) — reemplaza el
|
|
50298
|
+
// mapa hardcodeado `perm*`. Locale del momento de carga (igual que antes:
|
|
50299
|
+
// los labels se hornean en el RoleView; cambio de idioma requiere reload).
|
|
50300
|
+
const labeler = createPermissionLabeler(catalog, this.i18n.lang(), {
|
|
50301
|
+
allLabel: this.tt('permAll'),
|
|
50302
|
+
});
|
|
50303
|
+
this.roles.set((rawRoles ?? []).map((r) => this.buildRoleView({
|
|
50304
|
+
id: r.id,
|
|
50305
|
+
name: r.name,
|
|
50306
|
+
description: r.description,
|
|
50307
|
+
permissions: r.permissions ?? [],
|
|
50308
|
+
isSystem: r.isSystem ?? false,
|
|
50309
|
+
}, byResource, currentAppId, labeler)));
|
|
50310
|
+
this.loading.set(false);
|
|
50311
|
+
},
|
|
50312
|
+
error: err => {
|
|
50313
|
+
this.loadError.set(err);
|
|
50314
|
+
this.loading.set(false);
|
|
50315
|
+
},
|
|
50316
|
+
});
|
|
50082
50317
|
}
|
|
50083
|
-
|
|
50084
|
-
|
|
50318
|
+
buildRoleView(role, byResource, currentAppId, labeler) {
|
|
50319
|
+
const appPerms = [];
|
|
50320
|
+
const orgPerms = [];
|
|
50321
|
+
const otherByApp = new Map();
|
|
50322
|
+
for (const perm of role.permissions) {
|
|
50323
|
+
const [resource] = perm.split(':');
|
|
50324
|
+
const entry = byResource.get(resource);
|
|
50325
|
+
// Sin entrada en el catálogo (el backend omite los recursos internal para
|
|
50326
|
+
// no-platform-admins) o entrada internal → NUNCA se muestra a nivel de
|
|
50327
|
+
// producto. Defensa en profundidad junto al filtro del backend (ADR-024).
|
|
50328
|
+
// Nota: el rol puede traer el permiso aunque el recurso esté filtrado del
|
|
50329
|
+
// catálogo (ej. admin tiene templates:*); por eso se descarta lo no resuelto.
|
|
50330
|
+
if (!entry || entry.scope === 'internal') {
|
|
50331
|
+
continue;
|
|
50332
|
+
}
|
|
50333
|
+
const label = labeler(perm);
|
|
50334
|
+
if (entry.scope === 'app') {
|
|
50335
|
+
if (entry.appId === currentAppId) {
|
|
50336
|
+
appPerms.push(label);
|
|
50337
|
+
}
|
|
50338
|
+
else {
|
|
50339
|
+
const appId = entry.appId ?? '?';
|
|
50340
|
+
const arr = otherByApp.get(appId) ?? [];
|
|
50341
|
+
arr.push(label);
|
|
50342
|
+
otherByApp.set(appId, arr);
|
|
50343
|
+
}
|
|
50344
|
+
}
|
|
50345
|
+
else {
|
|
50346
|
+
// scope === 'org' → gestión de la organización del cliente
|
|
50347
|
+
orgPerms.push(label);
|
|
50348
|
+
}
|
|
50349
|
+
}
|
|
50350
|
+
const groups = [];
|
|
50351
|
+
if (appPerms.length) {
|
|
50352
|
+
groups.push({ key: 'app', label: this.tt('groupApp'), perms: appPerms });
|
|
50353
|
+
}
|
|
50354
|
+
if (orgPerms.length) {
|
|
50355
|
+
groups.push({
|
|
50356
|
+
key: 'org',
|
|
50357
|
+
label: this.tt('groupOrg'),
|
|
50358
|
+
perms: orgPerms,
|
|
50359
|
+
});
|
|
50360
|
+
}
|
|
50361
|
+
for (const [appId, perms] of otherByApp) {
|
|
50362
|
+
groups.push({
|
|
50363
|
+
key: `other:${appId}`,
|
|
50364
|
+
label: `${this.tt('groupOther')}: ${this.appLabel(appId)}`,
|
|
50365
|
+
perms,
|
|
50366
|
+
});
|
|
50367
|
+
}
|
|
50368
|
+
return {
|
|
50369
|
+
id: role.id,
|
|
50370
|
+
name: role.name,
|
|
50371
|
+
description: role.description,
|
|
50372
|
+
isSystem: role.isSystem,
|
|
50373
|
+
groups,
|
|
50374
|
+
hasPerms: groups.length > 0,
|
|
50375
|
+
};
|
|
50085
50376
|
}
|
|
50086
|
-
|
|
50087
|
-
return this.i18n.t(key, this.
|
|
50377
|
+
tt(key) {
|
|
50378
|
+
return this.i18n.t(key, this.ns);
|
|
50088
50379
|
}
|
|
50089
|
-
|
|
50090
|
-
|
|
50091
|
-
|
|
50092
|
-
|
|
50093
|
-
|
|
50094
|
-
|
|
50095
|
-
|
|
50096
|
-
|
|
50097
|
-
|
|
50098
|
-
|
|
50099
|
-
|
|
50100
|
-
|
|
50101
|
-
|
|
50102
|
-
|
|
50103
|
-
|
|
50104
|
-
|
|
50105
|
-
|
|
50106
|
-
|
|
50107
|
-
|
|
50108
|
-
|
|
50109
|
-
|
|
50110
|
-
|
|
50111
|
-
|
|
50112
|
-
|
|
50113
|
-
[props]="{ placeholder: t('searchPlaceholder'), debounce: 150 }"
|
|
50114
|
-
(filterEvent)="query.set($event)"
|
|
50115
|
-
/>
|
|
50116
|
-
|
|
50117
|
-
@if (members.length === 0) {
|
|
50118
|
-
<val-text
|
|
50119
|
-
[props]="{
|
|
50120
|
-
content: t('empty'),
|
|
50121
|
-
size: 'medium',
|
|
50122
|
-
color: 'medium',
|
|
50123
|
-
bold: false,
|
|
50124
|
-
}"
|
|
50125
|
-
/>
|
|
50126
|
-
} @else if (filtered().length === 0) {
|
|
50127
|
-
<val-text
|
|
50380
|
+
appLabel(appId) {
|
|
50381
|
+
const key = `app_${appId}`;
|
|
50382
|
+
const label = this.tt(key);
|
|
50383
|
+
return label === key ? appId : label;
|
|
50384
|
+
}
|
|
50385
|
+
roleLabel(name) {
|
|
50386
|
+
const map = {
|
|
50387
|
+
viewer: 'roleViewer',
|
|
50388
|
+
editor: 'roleEditor',
|
|
50389
|
+
admin: 'roleAdmin',
|
|
50390
|
+
super_admin: 'roleSuperAdmin',
|
|
50391
|
+
bingo_admin: 'roleBingoAdmin',
|
|
50392
|
+
bingo_tesorero: 'roleBingoTesorero',
|
|
50393
|
+
bingo_taquilla: 'roleBingoTaquilla',
|
|
50394
|
+
bingo_operator: 'roleBingoOperator',
|
|
50395
|
+
};
|
|
50396
|
+
return this.tt(map[name] ?? name);
|
|
50397
|
+
}
|
|
50398
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: PermissionsViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
50399
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: PermissionsViewComponent, isStandalone: true, selector: "val-permissions-view", inputs: { config: "config", isModal: "isModal" }, ngImport: i0, template: `
|
|
50400
|
+
<div class="page">
|
|
50401
|
+
<header class="page-header">
|
|
50402
|
+
<val-display [props]="{ size: 'small', color: 'dark', content: tt('pageTitle') }" />
|
|
50403
|
+
<val-title
|
|
50128
50404
|
[props]="{
|
|
50129
|
-
|
|
50130
|
-
|
|
50131
|
-
color: 'medium',
|
|
50405
|
+
size: 'large',
|
|
50406
|
+
color: 'dark',
|
|
50132
50407
|
bold: false,
|
|
50408
|
+
content: tt('pageDescription'),
|
|
50133
50409
|
}"
|
|
50134
50410
|
/>
|
|
50411
|
+
</header>
|
|
50412
|
+
|
|
50413
|
+
@if (loading()) {
|
|
50414
|
+
<div class="spinner-row">
|
|
50415
|
+
<ion-spinner name="crescent" />
|
|
50416
|
+
</div>
|
|
50417
|
+
} @else if (loadError()) {
|
|
50418
|
+
<div class="error-row">
|
|
50419
|
+
<val-text
|
|
50420
|
+
[props]="{
|
|
50421
|
+
content: tt('errorTitle'),
|
|
50422
|
+
size: 'medium',
|
|
50423
|
+
color: 'medium',
|
|
50424
|
+
bold: false,
|
|
50425
|
+
}"
|
|
50426
|
+
/>
|
|
50427
|
+
<val-button [props]="retryButtonProps()" (click)="load()" />
|
|
50428
|
+
</div>
|
|
50135
50429
|
} @else {
|
|
50136
|
-
|
|
50137
|
-
|
|
50138
|
-
<
|
|
50139
|
-
|
|
50140
|
-
|
|
50141
|
-
|
|
50142
|
-
|
|
50143
|
-
|
|
50144
|
-
|
|
50145
|
-
|
|
50146
|
-
|
|
50430
|
+
@if (resolvedConfig().showSystemRoles) {
|
|
50431
|
+
<section class="perm-section">
|
|
50432
|
+
<div class="perm-section__header">
|
|
50433
|
+
<val-title
|
|
50434
|
+
[props]="{
|
|
50435
|
+
size: 'medium',
|
|
50436
|
+
color: 'dark',
|
|
50437
|
+
bold: true,
|
|
50438
|
+
content: tt('systemRolesTitle'),
|
|
50439
|
+
}"
|
|
50440
|
+
/>
|
|
50441
|
+
<val-text
|
|
50442
|
+
[props]="{
|
|
50443
|
+
content: tt('systemRolesHint'),
|
|
50444
|
+
size: 'medium',
|
|
50445
|
+
color: 'medium',
|
|
50446
|
+
bold: false,
|
|
50447
|
+
}"
|
|
50448
|
+
/>
|
|
50449
|
+
</div>
|
|
50450
|
+
<div class="role-list">
|
|
50451
|
+
@for (role of systemRoles(); track role.id) {
|
|
50452
|
+
<div class="role-card">
|
|
50453
|
+
<div class="role-card__header">
|
|
50454
|
+
<span class="role-card__name">{{ roleLabel(role.name) }}</span>
|
|
50455
|
+
<val-pill
|
|
50456
|
+
[props]="{
|
|
50457
|
+
label: tt('systemBadge'),
|
|
50458
|
+
color: 'primary',
|
|
50459
|
+
weight: 'bold',
|
|
50460
|
+
}"
|
|
50461
|
+
/>
|
|
50462
|
+
</div>
|
|
50463
|
+
@if (role.description) {
|
|
50464
|
+
<span class="role-card__desc">{{ role.description }}</span>
|
|
50465
|
+
}
|
|
50466
|
+
@if (!role.hasPerms) {
|
|
50467
|
+
<div class="perm-chips">
|
|
50468
|
+
<span class="perm-chip perm-chip--empty">{{ tt('noPermissions') }}</span>
|
|
50469
|
+
</div>
|
|
50470
|
+
} @else {
|
|
50471
|
+
@for (g of role.groups; track g.key) {
|
|
50472
|
+
<div class="perm-group">
|
|
50473
|
+
<span class="perm-group__label">{{ g.label }}</span>
|
|
50474
|
+
<div class="perm-chips">
|
|
50475
|
+
@for (p of g.perms; track p) {
|
|
50476
|
+
<span class="perm-chip">{{ p }}</span>
|
|
50477
|
+
}
|
|
50478
|
+
</div>
|
|
50479
|
+
</div>
|
|
50480
|
+
}
|
|
50481
|
+
}
|
|
50482
|
+
</div>
|
|
50147
50483
|
}
|
|
50148
|
-
</
|
|
50149
|
-
|
|
50150
|
-
|
|
50151
|
-
|
|
50152
|
-
|
|
50153
|
-
|
|
50154
|
-
|
|
50155
|
-
|
|
50156
|
-
|
|
50157
|
-
|
|
50158
|
-
|
|
50159
|
-
|
|
50160
|
-
|
|
50161
|
-
|
|
50162
|
-
|
|
50163
|
-
|
|
50164
|
-
|
|
50165
|
-
|
|
50166
|
-
|
|
50167
|
-
|
|
50168
|
-
|
|
50169
|
-
|
|
50170
|
-
|
|
50171
|
-
|
|
50172
|
-
|
|
50173
|
-
|
|
50174
|
-
|
|
50175
|
-
|
|
50176
|
-
|
|
50177
|
-
|
|
50178
|
-
|
|
50179
|
-
|
|
50180
|
-
|
|
50181
|
-
|
|
50182
|
-
|
|
50183
|
-
|
|
50184
|
-
|
|
50185
|
-
|
|
50186
|
-
|
|
50187
|
-
|
|
50188
|
-
|
|
50484
|
+
</div>
|
|
50485
|
+
</section>
|
|
50486
|
+
}
|
|
50487
|
+
|
|
50488
|
+
@if (resolvedConfig().showCustomRoles) {
|
|
50489
|
+
<section class="perm-section">
|
|
50490
|
+
<div class="perm-section__header">
|
|
50491
|
+
<val-title
|
|
50492
|
+
[props]="{
|
|
50493
|
+
size: 'medium',
|
|
50494
|
+
color: 'dark',
|
|
50495
|
+
bold: true,
|
|
50496
|
+
content: tt('customRolesTitle'),
|
|
50497
|
+
}"
|
|
50498
|
+
/>
|
|
50499
|
+
<val-text
|
|
50500
|
+
[props]="{
|
|
50501
|
+
content: tt('customRolesHint'),
|
|
50502
|
+
size: 'medium',
|
|
50503
|
+
color: 'medium',
|
|
50504
|
+
bold: false,
|
|
50505
|
+
}"
|
|
50506
|
+
/>
|
|
50507
|
+
</div>
|
|
50508
|
+
@if (customRoles().length === 0) {
|
|
50509
|
+
<val-text
|
|
50510
|
+
[props]="{
|
|
50511
|
+
content: tt('noCustomRoles'),
|
|
50512
|
+
size: 'medium',
|
|
50513
|
+
color: 'medium',
|
|
50514
|
+
bold: false,
|
|
50515
|
+
}"
|
|
50516
|
+
/>
|
|
50517
|
+
} @else {
|
|
50518
|
+
<div class="role-list">
|
|
50519
|
+
@for (role of customRoles(); track role.id) {
|
|
50520
|
+
<div class="role-card">
|
|
50521
|
+
<div class="role-card__header">
|
|
50522
|
+
<span class="role-card__name">{{ role.name }}</span>
|
|
50523
|
+
</div>
|
|
50524
|
+
@if (role.description) {
|
|
50525
|
+
<span class="role-card__desc">{{ role.description }}</span>
|
|
50526
|
+
}
|
|
50527
|
+
@if (!role.hasPerms) {
|
|
50528
|
+
<div class="perm-chips">
|
|
50529
|
+
<span class="perm-chip perm-chip--empty">{{ tt('noPermissions') }}</span>
|
|
50530
|
+
</div>
|
|
50531
|
+
} @else {
|
|
50532
|
+
@for (g of role.groups; track g.key) {
|
|
50533
|
+
<div class="perm-group">
|
|
50534
|
+
<span class="perm-group__label">{{ g.label }}</span>
|
|
50535
|
+
<div class="perm-chips">
|
|
50536
|
+
@for (p of g.perms; track p) {
|
|
50537
|
+
<span class="perm-chip">{{ p }}</span>
|
|
50538
|
+
}
|
|
50539
|
+
</div>
|
|
50540
|
+
</div>
|
|
50541
|
+
}
|
|
50542
|
+
}
|
|
50543
|
+
</div>
|
|
50544
|
+
}
|
|
50545
|
+
</div>
|
|
50546
|
+
}
|
|
50547
|
+
</section>
|
|
50548
|
+
}
|
|
50549
|
+
}
|
|
50550
|
+
</div>
|
|
50551
|
+
`, isInline: true, styles: [".page{padding:16px 0;max-width:720px;margin:0 auto}.page-header{margin-bottom:24px}.spinner-row,.error-row{display:flex;flex-direction:column;align-items:center;gap:16px;padding:32px 0}.perm-section{margin-bottom:32px}.perm-section__header{margin-bottom:12px}.role-list{display:flex;flex-direction:column;gap:10px}.role-card{border:1.5px solid var(--ion-color-light);border-radius:12px;padding:14px 16px;display:flex;flex-direction:column;gap:10px}.role-card__header{display:flex;align-items:center;gap:8px;flex-wrap:wrap}.role-card__name{font-size:.9375rem;font-weight:600;color:var(--ion-color-dark)}.role-card__desc{font-size:.8125rem;color:var(--ion-color-medium);line-height:1.4}.perm-group{display:flex;flex-direction:column;gap:6px}.perm-group__label{font-size:.6875rem;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--ion-color-medium)}.perm-chips{display:flex;flex-wrap:wrap;gap:6px}.perm-chip{font-size:.75rem;padding:3px 10px;border-radius:20px;background:var(--ion-color-light);color:var(--ion-color-dark)}.perm-chip--empty{color:var(--ion-color-medium);background:transparent;padding-left:0}\n"], dependencies: [{ kind: "component", type: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { kind: "component", type: DisplayComponent, selector: "val-display", inputs: ["props"] }, { kind: "component", type: PillComponent, selector: "val-pill", inputs: ["preset", "props"], outputs: ["pillClick", "pillAction"] }, { kind: "component", type: TitleComponent, selector: "val-title", inputs: ["props"] }, { kind: "component", type: TextComponent, selector: "val-text", inputs: ["props"] }, { kind: "component", type: ButtonComponent, selector: "val-button", inputs: ["preset", "props"], outputs: ["onClick"] }] }); }
|
|
50552
|
+
}
|
|
50553
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: PermissionsViewComponent, decorators: [{
|
|
50554
|
+
type: Component,
|
|
50555
|
+
args: [{ selector: 'val-permissions-view', standalone: true, imports: [IonSpinner, DisplayComponent, PillComponent, TitleComponent, TextComponent, ButtonComponent], template: `
|
|
50556
|
+
<div class="page">
|
|
50557
|
+
<header class="page-header">
|
|
50558
|
+
<val-display [props]="{ size: 'small', color: 'dark', content: tt('pageTitle') }" />
|
|
50559
|
+
<val-title
|
|
50560
|
+
[props]="{
|
|
50561
|
+
size: 'large',
|
|
50562
|
+
color: 'dark',
|
|
50563
|
+
bold: false,
|
|
50564
|
+
content: tt('pageDescription'),
|
|
50565
|
+
}"
|
|
50566
|
+
/>
|
|
50567
|
+
</header>
|
|
50568
|
+
|
|
50569
|
+
@if (loading()) {
|
|
50570
|
+
<div class="spinner-row">
|
|
50571
|
+
<ion-spinner name="crescent" />
|
|
50572
|
+
</div>
|
|
50573
|
+
} @else if (loadError()) {
|
|
50574
|
+
<div class="error-row">
|
|
50575
|
+
<val-text
|
|
50576
|
+
[props]="{
|
|
50577
|
+
content: tt('errorTitle'),
|
|
50578
|
+
size: 'medium',
|
|
50579
|
+
color: 'medium',
|
|
50580
|
+
bold: false,
|
|
50581
|
+
}"
|
|
50582
|
+
/>
|
|
50583
|
+
<val-button [props]="retryButtonProps()" (click)="load()" />
|
|
50584
|
+
</div>
|
|
50585
|
+
} @else {
|
|
50586
|
+
@if (resolvedConfig().showSystemRoles) {
|
|
50587
|
+
<section class="perm-section">
|
|
50588
|
+
<div class="perm-section__header">
|
|
50589
|
+
<val-title
|
|
50590
|
+
[props]="{
|
|
50591
|
+
size: 'medium',
|
|
50592
|
+
color: 'dark',
|
|
50593
|
+
bold: true,
|
|
50594
|
+
content: tt('systemRolesTitle'),
|
|
50595
|
+
}"
|
|
50596
|
+
/>
|
|
50597
|
+
<val-text
|
|
50598
|
+
[props]="{
|
|
50599
|
+
content: tt('systemRolesHint'),
|
|
50600
|
+
size: 'medium',
|
|
50601
|
+
color: 'medium',
|
|
50602
|
+
bold: false,
|
|
50603
|
+
}"
|
|
50604
|
+
/>
|
|
50605
|
+
</div>
|
|
50606
|
+
<div class="role-list">
|
|
50607
|
+
@for (role of systemRoles(); track role.id) {
|
|
50608
|
+
<div class="role-card">
|
|
50609
|
+
<div class="role-card__header">
|
|
50610
|
+
<span class="role-card__name">{{ roleLabel(role.name) }}</span>
|
|
50611
|
+
<val-pill
|
|
50612
|
+
[props]="{
|
|
50613
|
+
label: tt('systemBadge'),
|
|
50614
|
+
color: 'primary',
|
|
50615
|
+
weight: 'bold',
|
|
50616
|
+
}"
|
|
50617
|
+
/>
|
|
50618
|
+
</div>
|
|
50619
|
+
@if (role.description) {
|
|
50620
|
+
<span class="role-card__desc">{{ role.description }}</span>
|
|
50621
|
+
}
|
|
50622
|
+
@if (!role.hasPerms) {
|
|
50623
|
+
<div class="perm-chips">
|
|
50624
|
+
<span class="perm-chip perm-chip--empty">{{ tt('noPermissions') }}</span>
|
|
50625
|
+
</div>
|
|
50626
|
+
} @else {
|
|
50627
|
+
@for (g of role.groups; track g.key) {
|
|
50628
|
+
<div class="perm-group">
|
|
50629
|
+
<span class="perm-group__label">{{ g.label }}</span>
|
|
50630
|
+
<div class="perm-chips">
|
|
50631
|
+
@for (p of g.perms; track p) {
|
|
50632
|
+
<span class="perm-chip">{{ p }}</span>
|
|
50633
|
+
}
|
|
50634
|
+
</div>
|
|
50635
|
+
</div>
|
|
50636
|
+
}
|
|
50637
|
+
}
|
|
50638
|
+
</div>
|
|
50639
|
+
}
|
|
50640
|
+
</div>
|
|
50641
|
+
</section>
|
|
50642
|
+
}
|
|
50643
|
+
|
|
50644
|
+
@if (resolvedConfig().showCustomRoles) {
|
|
50645
|
+
<section class="perm-section">
|
|
50646
|
+
<div class="perm-section__header">
|
|
50647
|
+
<val-title
|
|
50648
|
+
[props]="{
|
|
50649
|
+
size: 'medium',
|
|
50650
|
+
color: 'dark',
|
|
50651
|
+
bold: true,
|
|
50652
|
+
content: tt('customRolesTitle'),
|
|
50653
|
+
}"
|
|
50654
|
+
/>
|
|
50655
|
+
<val-text
|
|
50656
|
+
[props]="{
|
|
50657
|
+
content: tt('customRolesHint'),
|
|
50658
|
+
size: 'medium',
|
|
50659
|
+
color: 'medium',
|
|
50660
|
+
bold: false,
|
|
50661
|
+
}"
|
|
50662
|
+
/>
|
|
50663
|
+
</div>
|
|
50664
|
+
@if (customRoles().length === 0) {
|
|
50665
|
+
<val-text
|
|
50666
|
+
[props]="{
|
|
50667
|
+
content: tt('noCustomRoles'),
|
|
50668
|
+
size: 'medium',
|
|
50669
|
+
color: 'medium',
|
|
50670
|
+
bold: false,
|
|
50671
|
+
}"
|
|
50672
|
+
/>
|
|
50673
|
+
} @else {
|
|
50674
|
+
<div class="role-list">
|
|
50675
|
+
@for (role of customRoles(); track role.id) {
|
|
50676
|
+
<div class="role-card">
|
|
50677
|
+
<div class="role-card__header">
|
|
50678
|
+
<span class="role-card__name">{{ role.name }}</span>
|
|
50679
|
+
</div>
|
|
50680
|
+
@if (role.description) {
|
|
50681
|
+
<span class="role-card__desc">{{ role.description }}</span>
|
|
50682
|
+
}
|
|
50683
|
+
@if (!role.hasPerms) {
|
|
50684
|
+
<div class="perm-chips">
|
|
50685
|
+
<span class="perm-chip perm-chip--empty">{{ tt('noPermissions') }}</span>
|
|
50686
|
+
</div>
|
|
50687
|
+
} @else {
|
|
50688
|
+
@for (g of role.groups; track g.key) {
|
|
50689
|
+
<div class="perm-group">
|
|
50690
|
+
<span class="perm-group__label">{{ g.label }}</span>
|
|
50691
|
+
<div class="perm-chips">
|
|
50692
|
+
@for (p of g.perms; track p) {
|
|
50693
|
+
<span class="perm-chip">{{ p }}</span>
|
|
50694
|
+
}
|
|
50695
|
+
</div>
|
|
50696
|
+
</div>
|
|
50697
|
+
}
|
|
50698
|
+
}
|
|
50699
|
+
</div>
|
|
50700
|
+
}
|
|
50701
|
+
</div>
|
|
50702
|
+
}
|
|
50703
|
+
</section>
|
|
50704
|
+
}
|
|
50705
|
+
}
|
|
50706
|
+
</div>
|
|
50707
|
+
`, styles: [".page{padding:16px 0;max-width:720px;margin:0 auto}.page-header{margin-bottom:24px}.spinner-row,.error-row{display:flex;flex-direction:column;align-items:center;gap:16px;padding:32px 0}.perm-section{margin-bottom:32px}.perm-section__header{margin-bottom:12px}.role-list{display:flex;flex-direction:column;gap:10px}.role-card{border:1.5px solid var(--ion-color-light);border-radius:12px;padding:14px 16px;display:flex;flex-direction:column;gap:10px}.role-card__header{display:flex;align-items:center;gap:8px;flex-wrap:wrap}.role-card__name{font-size:.9375rem;font-weight:600;color:var(--ion-color-dark)}.role-card__desc{font-size:.8125rem;color:var(--ion-color-medium);line-height:1.4}.perm-group{display:flex;flex-direction:column;gap:6px}.perm-group__label{font-size:.6875rem;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--ion-color-medium)}.perm-chips{display:flex;flex-wrap:wrap;gap:6px}.perm-chip{font-size:.75rem;padding:3px 10px;border-radius:20px;background:var(--ion-color-light);color:var(--ion-color-dark)}.perm-chip--empty{color:var(--ion-color-medium);background:transparent;padding-left:0}\n"] }]
|
|
50708
|
+
}], ctorParameters: () => [], propDecorators: { config: [{
|
|
50709
|
+
type: Input
|
|
50710
|
+
}], isModal: [{
|
|
50711
|
+
type: Input
|
|
50712
|
+
}] } });
|
|
50713
|
+
|
|
50714
|
+
class PermissionsModalComponent {
|
|
50715
|
+
constructor() {
|
|
50716
|
+
this.i18n = inject(I18nService);
|
|
50717
|
+
this.isOpen = false;
|
|
50718
|
+
this.dismissed = new EventEmitter();
|
|
50719
|
+
}
|
|
50720
|
+
close() {
|
|
50721
|
+
this.dismissed.emit();
|
|
50722
|
+
}
|
|
50723
|
+
t(key) {
|
|
50724
|
+
return this.i18n.t(key, '_global');
|
|
50725
|
+
}
|
|
50726
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: PermissionsModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
50727
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: PermissionsModalComponent, isStandalone: true, selector: "val-permissions-modal", inputs: { isOpen: "isOpen", config: "config" }, outputs: { dismissed: "dismissed" }, ngImport: i0, template: `
|
|
50728
|
+
<ion-modal [isOpen]="isOpen" (didDismiss)="close()" cssClass="val-modal-adaptive">
|
|
50729
|
+
<ng-template>
|
|
50730
|
+
<ion-header>
|
|
50731
|
+
<ion-toolbar>
|
|
50732
|
+
<ion-buttons slot="end">
|
|
50733
|
+
<ion-button fill="clear" color="dark" shape="round" (click)="close()">
|
|
50734
|
+
<strong>{{ t('close') }}</strong>
|
|
50735
|
+
</ion-button>
|
|
50736
|
+
</ion-buttons>
|
|
50737
|
+
</ion-toolbar>
|
|
50738
|
+
</ion-header>
|
|
50739
|
+
<ion-content class="ion-padding">
|
|
50740
|
+
<val-permissions-view [config]="config" [isModal]="true" />
|
|
50741
|
+
</ion-content>
|
|
50742
|
+
</ng-template>
|
|
50743
|
+
</ion-modal>
|
|
50744
|
+
`, isInline: true, dependencies: [{ kind: "component", type: IonModal, selector: "ion-modal" }, { kind: "component", type: IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "component", type: IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: PermissionsViewComponent, selector: "val-permissions-view", inputs: ["config", "isModal"] }] }); }
|
|
50745
|
+
}
|
|
50746
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: PermissionsModalComponent, decorators: [{
|
|
50747
|
+
type: Component,
|
|
50748
|
+
args: [{
|
|
50749
|
+
selector: 'val-permissions-modal',
|
|
50750
|
+
standalone: true,
|
|
50751
|
+
imports: [IonModal, IonHeader, IonToolbar, IonButtons, IonButton, IonContent, PermissionsViewComponent],
|
|
50752
|
+
template: `
|
|
50753
|
+
<ion-modal [isOpen]="isOpen" (didDismiss)="close()" cssClass="val-modal-adaptive">
|
|
50754
|
+
<ng-template>
|
|
50755
|
+
<ion-header>
|
|
50756
|
+
<ion-toolbar>
|
|
50757
|
+
<ion-buttons slot="end">
|
|
50758
|
+
<ion-button fill="clear" color="dark" shape="round" (click)="close()">
|
|
50759
|
+
<strong>{{ t('close') }}</strong>
|
|
50760
|
+
</ion-button>
|
|
50761
|
+
</ion-buttons>
|
|
50762
|
+
</ion-toolbar>
|
|
50763
|
+
</ion-header>
|
|
50764
|
+
<ion-content class="ion-padding">
|
|
50765
|
+
<val-permissions-view [config]="config" [isModal]="true" />
|
|
50766
|
+
</ion-content>
|
|
50767
|
+
</ng-template>
|
|
50768
|
+
</ion-modal>
|
|
50769
|
+
`,
|
|
50770
|
+
}]
|
|
50771
|
+
}], propDecorators: { isOpen: [{
|
|
50772
|
+
type: Input
|
|
50773
|
+
}], config: [{
|
|
50774
|
+
type: Input
|
|
50775
|
+
}], dismissed: [{
|
|
50776
|
+
type: Output
|
|
50777
|
+
}] } });
|
|
50778
|
+
|
|
50779
|
+
/**
|
|
50780
|
+
* Defaults i18n (es/en) embebidos en `val-transfer-ownership-modal`.
|
|
50781
|
+
* Auto-registrados en el constructor del componente si el consumer no proveyó el
|
|
50782
|
+
* namespace (`Settings.TransferModal` por default). SOLO incluye las keys que el
|
|
50783
|
+
* modal usa.
|
|
50784
|
+
*/
|
|
50785
|
+
const TRANSFER_OWNERSHIP_MODAL_I18N = {
|
|
50786
|
+
es: {
|
|
50787
|
+
title: 'Transferir propiedad',
|
|
50788
|
+
hint: 'Selecciona el nuevo propietario de la organización.',
|
|
50789
|
+
searchPlaceholder: 'Buscar miembro...',
|
|
50790
|
+
empty: 'No hay otros miembros disponibles.',
|
|
50791
|
+
noResults: 'Sin resultados para',
|
|
50792
|
+
confirmCta: 'Confirmar transferencia',
|
|
50793
|
+
close: 'Cancelar',
|
|
50794
|
+
},
|
|
50795
|
+
en: {
|
|
50796
|
+
title: 'Transfer ownership',
|
|
50797
|
+
hint: 'Select the new owner of the organization.',
|
|
50798
|
+
searchPlaceholder: 'Search member...',
|
|
50799
|
+
empty: 'No other members available.',
|
|
50800
|
+
noResults: 'No results for',
|
|
50801
|
+
confirmCta: 'Confirm transfer',
|
|
50802
|
+
close: 'Cancel',
|
|
50803
|
+
},
|
|
50804
|
+
};
|
|
50805
|
+
|
|
50806
|
+
const DEFAULT_NAMESPACE$8 = 'Settings.TransferModal';
|
|
50807
|
+
/**
|
|
50808
|
+
* `val-transfer-ownership-modal` — selector de miembro para transferir la
|
|
50809
|
+
* propiedad de la organización. Promovido desde `showcase` bajo el proceso de
|
|
50810
|
+
* ADR-021.
|
|
50811
|
+
*
|
|
50812
|
+
* Recibe la lista de candidatos por `members` (todos excepto el owner actual),
|
|
50813
|
+
* permite filtrarlos con `val-searchbar` y, al confirmar, ejecuta el callback
|
|
50814
|
+
* `onConfirm(newOwnerId)`. La ejecución real de la transferencia (vía
|
|
50815
|
+
* `OrgService.transferOwnership`) queda en el consumer.
|
|
50816
|
+
*
|
|
50817
|
+
* Abierto vía `ModalService` — recibe `_modalRef` por `componentProps`. Header
|
|
50818
|
+
* canónico (Regla #5): sin `ion-title`, botón Cerrar texto en `slot=end`,
|
|
50819
|
+
* título/subtítulo en el body con `val-display`/`val-title`.
|
|
50820
|
+
*
|
|
50821
|
+
* Auto-registra sus defaults i18n (es/en) si el consumer no proveyó el namespace
|
|
50822
|
+
* `Settings.TransferModal`.
|
|
50823
|
+
*/
|
|
50824
|
+
class TransferOwnershipModalComponent {
|
|
50825
|
+
constructor() {
|
|
50826
|
+
this.i18n = inject(I18nService);
|
|
50827
|
+
/** Lista de miembros transferibles (todos excepto el owner actual). */
|
|
50828
|
+
this.members = [];
|
|
50829
|
+
/** Namespace i18n con que la vista resuelve sus textos. */
|
|
50830
|
+
this.i18nNamespace = DEFAULT_NAMESPACE$8;
|
|
50831
|
+
this.query = signal('');
|
|
50832
|
+
this.selectedId = signal('');
|
|
50833
|
+
this.filtered = computed(() => {
|
|
50834
|
+
const q = this.query().trim().toLowerCase();
|
|
50835
|
+
if (!q)
|
|
50836
|
+
return this.members;
|
|
50837
|
+
return this.members.filter(m => m.name?.toLowerCase().includes(q) || m.email?.toLowerCase().includes(q) || m.userId.toLowerCase().includes(q));
|
|
50838
|
+
});
|
|
50839
|
+
this.noResultsLabel = computed(() => `${this.t('noResults')} "${this.query()}"`);
|
|
50840
|
+
if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$8)) {
|
|
50841
|
+
this.i18n.registerContent(DEFAULT_NAMESPACE$8, TRANSFER_OWNERSHIP_MODAL_I18N);
|
|
50842
|
+
}
|
|
50843
|
+
}
|
|
50844
|
+
ngOnInit() {
|
|
50845
|
+
if (!this.i18n.hasNamespace(this.i18nNamespace)) {
|
|
50846
|
+
this.i18n.registerContent(this.i18nNamespace, TRANSFER_OWNERSHIP_MODAL_I18N);
|
|
50847
|
+
}
|
|
50848
|
+
}
|
|
50849
|
+
confirm() {
|
|
50850
|
+
const id = this.selectedId();
|
|
50851
|
+
if (!id)
|
|
50852
|
+
return;
|
|
50853
|
+
this.onConfirm?.(id);
|
|
50854
|
+
this.dismiss();
|
|
50855
|
+
}
|
|
50856
|
+
dismiss() {
|
|
50857
|
+
this._modalRef?.dismiss(null, 'cancel');
|
|
50858
|
+
}
|
|
50859
|
+
t(key) {
|
|
50860
|
+
return this.i18n.t(key, this.i18nNamespace);
|
|
50861
|
+
}
|
|
50862
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: TransferOwnershipModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
50863
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: TransferOwnershipModalComponent, isStandalone: true, selector: "val-transfer-ownership-modal", inputs: { members: "members", onConfirm: "onConfirm", _modalRef: "_modalRef", i18nNamespace: "i18nNamespace" }, ngImport: i0, template: `
|
|
50864
|
+
<ion-header>
|
|
50865
|
+
<ion-toolbar>
|
|
50866
|
+
<ion-buttons slot="end">
|
|
50867
|
+
<ion-button fill="clear" color="dark" shape="round" (click)="dismiss()">
|
|
50868
|
+
<strong>{{ t('close') }}</strong>
|
|
50869
|
+
</ion-button>
|
|
50870
|
+
</ion-buttons>
|
|
50871
|
+
</ion-toolbar>
|
|
50872
|
+
</ion-header>
|
|
50873
|
+
|
|
50874
|
+
<ion-content class="ion-padding">
|
|
50875
|
+
<val-display [props]="{ content: t('title'), size: 'small', color: 'dark' }" />
|
|
50876
|
+
<val-title
|
|
50877
|
+
[props]="{
|
|
50878
|
+
content: t('hint'),
|
|
50879
|
+
size: 'large',
|
|
50880
|
+
color: '',
|
|
50881
|
+
bold: false,
|
|
50882
|
+
}"
|
|
50883
|
+
/>
|
|
50884
|
+
|
|
50885
|
+
<val-searchbar
|
|
50886
|
+
[props]="{ placeholder: t('searchPlaceholder'), debounce: 150 }"
|
|
50887
|
+
(filterEvent)="query.set($event)"
|
|
50888
|
+
/>
|
|
50889
|
+
|
|
50890
|
+
@if (members.length === 0) {
|
|
50891
|
+
<val-text
|
|
50892
|
+
[props]="{
|
|
50893
|
+
content: t('empty'),
|
|
50894
|
+
size: 'medium',
|
|
50895
|
+
color: 'medium',
|
|
50896
|
+
bold: false,
|
|
50897
|
+
}"
|
|
50898
|
+
/>
|
|
50899
|
+
} @else if (filtered().length === 0) {
|
|
50900
|
+
<val-text
|
|
50901
|
+
[props]="{
|
|
50902
|
+
content: noResultsLabel(),
|
|
50903
|
+
size: 'medium',
|
|
50904
|
+
color: 'medium',
|
|
50905
|
+
bold: false,
|
|
50906
|
+
}"
|
|
50907
|
+
/>
|
|
50908
|
+
} @else {
|
|
50909
|
+
<div class="transfer-member-list">
|
|
50910
|
+
@for (m of filtered(); track m.userId) {
|
|
50911
|
+
<button
|
|
50912
|
+
class="transfer-member-row"
|
|
50913
|
+
[class.transfer-member-row--selected]="selectedId() === m.userId"
|
|
50914
|
+
(click)="selectedId.set(m.userId)"
|
|
50915
|
+
type="button"
|
|
50916
|
+
>
|
|
50917
|
+
<span class="transfer-member-name">{{ m.name || m.email || m.userId }}</span>
|
|
50918
|
+
@if (selectedId() === m.userId) {
|
|
50919
|
+
<span class="transfer-member-check">✓</span>
|
|
50920
|
+
}
|
|
50921
|
+
</button>
|
|
50922
|
+
}
|
|
50923
|
+
</div>
|
|
50924
|
+
}
|
|
50925
|
+
|
|
50926
|
+
<div class="transfer-confirm-row">
|
|
50927
|
+
<ion-button expand="block" color="dark" shape="round" [disabled]="!selectedId()" (click)="confirm()">
|
|
50928
|
+
{{ t('confirmCta') }}
|
|
50929
|
+
</ion-button>
|
|
50930
|
+
</div>
|
|
50931
|
+
|
|
50932
|
+
<div class="modal-close-bottom">
|
|
50933
|
+
<ion-button expand="block" fill="outline" color="dark" shape="round" (click)="dismiss()">
|
|
50934
|
+
{{ t('close') }}
|
|
50935
|
+
</ion-button>
|
|
50936
|
+
</div>
|
|
50937
|
+
</ion-content>
|
|
50938
|
+
`, isInline: true, styles: ["val-display{display:block;margin-bottom:4px}val-title{display:block;margin-bottom:16px}val-searchbar{display:block;margin-bottom:12px}.transfer-member-list{display:flex;flex-direction:column;gap:4px}.transfer-member-row{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;border-radius:10px;border:1.5px solid var(--ion-color-light);background:transparent;cursor:pointer;width:100%;text-align:left;font-size:15px;color:var(--ion-color-dark);transition:border-color .15s}.transfer-member-row--selected{border-color:var(--ion-color-dark);background:var(--ion-color-light)}.transfer-member-check{font-size:17px;color:var(--ion-color-dark)}.transfer-confirm-row{margin-top:24px}.modal-close-bottom{margin-top:12px}\n"], dependencies: [{ kind: "component", type: IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "component", type: IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: SearchbarComponent, selector: "val-searchbar", inputs: ["preset", "props"], outputs: ["filterEvent", "focusEvent", "blurEvent"] }, { kind: "component", type: DisplayComponent, selector: "val-display", inputs: ["props"] }, { kind: "component", type: TextComponent, selector: "val-text", inputs: ["props"] }, { kind: "component", type: TitleComponent, selector: "val-title", inputs: ["props"] }] }); }
|
|
50939
|
+
}
|
|
50940
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: TransferOwnershipModalComponent, decorators: [{
|
|
50941
|
+
type: Component,
|
|
50942
|
+
args: [{ selector: 'val-transfer-ownership-modal', standalone: true, imports: [
|
|
50943
|
+
IonHeader,
|
|
50944
|
+
IonToolbar,
|
|
50945
|
+
IonButtons,
|
|
50946
|
+
IonButton,
|
|
50947
|
+
IonContent,
|
|
50948
|
+
SearchbarComponent,
|
|
50949
|
+
DisplayComponent,
|
|
50950
|
+
TextComponent,
|
|
50951
|
+
TitleComponent,
|
|
50952
|
+
], template: `
|
|
50953
|
+
<ion-header>
|
|
50954
|
+
<ion-toolbar>
|
|
50955
|
+
<ion-buttons slot="end">
|
|
50956
|
+
<ion-button fill="clear" color="dark" shape="round" (click)="dismiss()">
|
|
50957
|
+
<strong>{{ t('close') }}</strong>
|
|
50958
|
+
</ion-button>
|
|
50959
|
+
</ion-buttons>
|
|
50960
|
+
</ion-toolbar>
|
|
50961
|
+
</ion-header>
|
|
50189
50962
|
|
|
50190
50963
|
<ion-content class="ion-padding">
|
|
50191
50964
|
<val-display [props]="{ content: t('title'), size: 'small', color: 'dark' }" />
|
|
@@ -50341,7 +51114,7 @@ const MEMBER_IMPORT_MODAL_I18N = {
|
|
|
50341
51114
|
},
|
|
50342
51115
|
};
|
|
50343
51116
|
|
|
50344
|
-
const DEFAULT_NAMESPACE$
|
|
51117
|
+
const DEFAULT_NAMESPACE$7 = 'Settings.ImportModal';
|
|
50345
51118
|
addIcons({ cloudUploadOutline, documentTextOutline });
|
|
50346
51119
|
/**
|
|
50347
51120
|
* `val-member-import-modal` — panel de carga masiva de miembros (ADR-023 fase 6).
|
|
@@ -50361,7 +51134,7 @@ class MemberImportModalComponent {
|
|
|
50361
51134
|
/** Nombres de roles disponibles (para el hint). Opcional. */
|
|
50362
51135
|
this.availableRoles = [];
|
|
50363
51136
|
/** Namespace i18n. */
|
|
50364
|
-
this.i18nNamespace = DEFAULT_NAMESPACE$
|
|
51137
|
+
this.i18nNamespace = DEFAULT_NAMESPACE$7;
|
|
50365
51138
|
this.csv = '';
|
|
50366
51139
|
this.importMode = signal('file');
|
|
50367
51140
|
this.onConflictControl = new FormControl('assignRole');
|
|
@@ -50403,8 +51176,8 @@ class MemberImportModalComponent {
|
|
|
50403
51176
|
return this.t('summary');
|
|
50404
51177
|
return `${this.t('summary')}: ${s.created} ${this.t('created')} · ${s.assigned} ${this.t('assigned')} · ${s.skipped} ${this.t('skipped')} · ${s.failed} ${this.t('failed')}`;
|
|
50405
51178
|
});
|
|
50406
|
-
if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$
|
|
50407
|
-
this.i18n.registerContent(DEFAULT_NAMESPACE$
|
|
51179
|
+
if (!this.i18n.hasNamespace(DEFAULT_NAMESPACE$7)) {
|
|
51180
|
+
this.i18n.registerContent(DEFAULT_NAMESPACE$7, MEMBER_IMPORT_MODAL_I18N);
|
|
50408
51181
|
}
|
|
50409
51182
|
}
|
|
50410
51183
|
ngOnInit() {
|
|
@@ -50996,7 +51769,7 @@ const ORGANIZATION_VIEW_I18N = {
|
|
|
50996
51769
|
},
|
|
50997
51770
|
};
|
|
50998
51771
|
|
|
50999
|
-
const DEFAULT_NAMESPACE$
|
|
51772
|
+
const DEFAULT_NAMESPACE$6 = 'Settings.Organization';
|
|
51000
51773
|
const DEFAULT_VIEW_PERMISSIONS_ROUTE = '/app/settings/permissions';
|
|
51001
51774
|
const DEFAULT_API_KEYS_ROUTE = '/app/settings/api-keys';
|
|
51002
51775
|
const DEFAULT_LEAVE_REDIRECT_ROUTE = '/app/settings/account';
|
|
@@ -51054,7 +51827,7 @@ class OrganizationViewComponent {
|
|
|
51054
51827
|
showInvite: merged.showInvite ?? true,
|
|
51055
51828
|
showTransferOwnership: merged.showTransferOwnership ?? true,
|
|
51056
51829
|
showLeave: merged.showLeave ?? true,
|
|
51057
|
-
i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$
|
|
51830
|
+
i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$6,
|
|
51058
51831
|
viewPermissionsRoute: merged.viewPermissionsRoute ?? DEFAULT_VIEW_PERMISSIONS_ROUTE,
|
|
51059
51832
|
apiKeysRoute: merged.apiKeysRoute ?? DEFAULT_API_KEYS_ROUTE,
|
|
51060
51833
|
leaveRedirectRoute: merged.leaveRedirectRoute ?? DEFAULT_LEAVE_REDIRECT_ROUTE,
|
|
@@ -51067,6 +51840,7 @@ class OrganizationViewComponent {
|
|
|
51067
51840
|
this.org = signal(null);
|
|
51068
51841
|
this.loading = signal(false);
|
|
51069
51842
|
this.leaving = signal(false);
|
|
51843
|
+
this.permissionsOpen = signal(false);
|
|
51070
51844
|
this.orgLoadError = signal(null);
|
|
51071
51845
|
this.orgErrorState = computed(() => {
|
|
51072
51846
|
this.i18n.lang();
|
|
@@ -51365,7 +52139,7 @@ class OrganizationViewComponent {
|
|
|
51365
52139
|
});
|
|
51366
52140
|
}
|
|
51367
52141
|
onViewPermissions() {
|
|
51368
|
-
this.
|
|
52142
|
+
this.permissionsOpen.set(true);
|
|
51369
52143
|
}
|
|
51370
52144
|
onOpenTransfer() {
|
|
51371
52145
|
void this.modalService.openAdaptive({
|
|
@@ -51838,8 +52612,13 @@ class OrganizationViewComponent {
|
|
|
51838
52612
|
</div>
|
|
51839
52613
|
</section>
|
|
51840
52614
|
}
|
|
52615
|
+
<val-permissions-modal
|
|
52616
|
+
[isOpen]="permissionsOpen()"
|
|
52617
|
+
[config]="{ i18nNamespace: resolvedConfig().i18nNamespace }"
|
|
52618
|
+
(dismissed)="permissionsOpen.set(false)"
|
|
52619
|
+
/>
|
|
51841
52620
|
</div>
|
|
51842
|
-
`, isInline: true, styles: [".page{padding:16px 0;max-width:720px;margin:0 auto}.org-more-info-link{background:none;border:none;padding:4px 0;margin-top:4px;font-size:13px;font-weight:600;color:var(--ion-color-primary, #7026df);cursor:pointer;text-align:left}.page-header{margin-bottom:16px}.settings-section{padding:16px 0}.settings-section+.settings-section{border-top:1px solid var(--val-border-color, rgba(0, 0, 0, .08))}.section-header-row{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:8px}.section-body{display:flex;flex-direction:column;gap:10px}.row-actions{margin-top:12px}.row-actions--gap{display:flex;gap:8px}.invite-cta{display:flex;flex-direction:column;gap:16px}.invite-cta__text{display:flex;flex-direction:column;gap:4px}.invite-cta__actions{display:flex;gap:12px;flex-wrap:wrap}.org-info-card{border-radius:14px;background:var(--ion-color-light, #f4f5f8);overflow:hidden}:host-context(body.dark) .org-info-card,:host-context(html.ion-palette-dark) .org-info-card,:host-context([data-theme=\"dark\"]) .org-info-card{background:#ffffff0d}.org-info-field{padding:12px 16px;display:flex;flex-direction:column;gap:4px;border-bottom:1px solid var(--val-border-color, rgba(0, 0, 0, .06))}.org-info-field:last-child{border-bottom:none}.org-info-label{font-size:.74rem;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:var(--ion-color-medium)}.org-info-value{font-size:.95rem;font-weight:500;color:var(--ion-color-dark)}.org-info-value--muted{color:var(--ion-color-medium);font-weight:400}.plan-badge{display:inline-block;font-size:.78rem;font-weight:700;padding:3px 10px;border-radius:20px;width:fit-content}.plan-badge--free{background:var(--ion-color-light-shade, #d7d8da);color:var(--ion-color-dark)}.plan-badge--pro{background:var(--ion-color-primary);color:#fff}.plan-badge--enterprise{background:#f5c542;color:#222}.members-list{display:flex;flex-direction:column;gap:8px}.members-show-more{background:none;border:none;color:var(--ion-color-primary);font-size:14px;font-weight:500;cursor:pointer;padding:8px 0}.rbac-debug{opacity:.7}.rbac-debug__body{display:flex;flex-direction:column;gap:6px;font-family:monospace;font-size:.78rem}.rbac-debug__row{display:flex;gap:8px;align-items:flex-start}.rbac-debug__label{color:var(--ion-color-medium);min-width:72px;flex-shrink:0}.rbac-debug__value{color:var(--ion-color-dark);word-break:break-all}.rbac-debug__value--perms{color:var(--ion-color-medium)}\n"], dependencies: [{ kind: "component", type: DisplayComponent, selector: "val-display", inputs: ["props"] }, { kind: "component", type: EmptyStateComponent, selector: "val-empty-state", inputs: ["props"] }, { kind: "directive", type: HasPermissionDirective, selector: "[valHasPermission]", inputs: ["valHasPermission"] }, { kind: "component", type: SkeletonLayoutComponent, selector: "val-skeleton-layout", inputs: ["props"] }, { kind: "component", type: TitleComponent, selector: "val-title", inputs: ["props"] }, { kind: "component", type: TextComponent, selector: "val-text", inputs: ["props"] }, { kind: "component", type: ButtonComponent, selector: "val-button", inputs: ["preset", "props"], outputs: ["onClick"] }, { kind: "component", type: MemberCardComponent, selector: "val-member-card", inputs: ["props"], outputs: ["onAction"] }] }); }
|
|
52621
|
+
`, isInline: true, styles: [".page{padding:16px 0;max-width:720px;margin:0 auto}.org-more-info-link{background:none;border:none;padding:4px 0;margin-top:4px;font-size:13px;font-weight:600;color:var(--ion-color-primary, #7026df);cursor:pointer;text-align:left}.page-header{margin-bottom:16px}.settings-section{padding:16px 0}.settings-section+.settings-section{border-top:1px solid var(--val-border-color, rgba(0, 0, 0, .08))}.section-header-row{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:8px}.section-body{display:flex;flex-direction:column;gap:10px}.row-actions{margin-top:12px}.row-actions--gap{display:flex;gap:8px}.invite-cta{display:flex;flex-direction:column;gap:16px}.invite-cta__text{display:flex;flex-direction:column;gap:4px}.invite-cta__actions{display:flex;gap:12px;flex-wrap:wrap}.org-info-card{border-radius:14px;background:var(--ion-color-light, #f4f5f8);overflow:hidden}:host-context(body.dark) .org-info-card,:host-context(html.ion-palette-dark) .org-info-card,:host-context([data-theme=\"dark\"]) .org-info-card{background:#ffffff0d}.org-info-field{padding:12px 16px;display:flex;flex-direction:column;gap:4px;border-bottom:1px solid var(--val-border-color, rgba(0, 0, 0, .06))}.org-info-field:last-child{border-bottom:none}.org-info-label{font-size:.74rem;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:var(--ion-color-medium)}.org-info-value{font-size:.95rem;font-weight:500;color:var(--ion-color-dark)}.org-info-value--muted{color:var(--ion-color-medium);font-weight:400}.plan-badge{display:inline-block;font-size:.78rem;font-weight:700;padding:3px 10px;border-radius:20px;width:fit-content}.plan-badge--free{background:var(--ion-color-light-shade, #d7d8da);color:var(--ion-color-dark)}.plan-badge--pro{background:var(--ion-color-primary);color:#fff}.plan-badge--enterprise{background:#f5c542;color:#222}.members-list{display:flex;flex-direction:column;gap:8px}.members-show-more{background:none;border:none;color:var(--ion-color-primary);font-size:14px;font-weight:500;cursor:pointer;padding:8px 0}.rbac-debug{opacity:.7}.rbac-debug__body{display:flex;flex-direction:column;gap:6px;font-family:monospace;font-size:.78rem}.rbac-debug__row{display:flex;gap:8px;align-items:flex-start}.rbac-debug__label{color:var(--ion-color-medium);min-width:72px;flex-shrink:0}.rbac-debug__value{color:var(--ion-color-dark);word-break:break-all}.rbac-debug__value--perms{color:var(--ion-color-medium)}\n"], dependencies: [{ kind: "component", type: DisplayComponent, selector: "val-display", inputs: ["props"] }, { kind: "component", type: EmptyStateComponent, selector: "val-empty-state", inputs: ["props"] }, { kind: "directive", type: HasPermissionDirective, selector: "[valHasPermission]", inputs: ["valHasPermission"] }, { kind: "component", type: SkeletonLayoutComponent, selector: "val-skeleton-layout", inputs: ["props"] }, { kind: "component", type: TitleComponent, selector: "val-title", inputs: ["props"] }, { kind: "component", type: TextComponent, selector: "val-text", inputs: ["props"] }, { kind: "component", type: ButtonComponent, selector: "val-button", inputs: ["preset", "props"], outputs: ["onClick"] }, { kind: "component", type: MemberCardComponent, selector: "val-member-card", inputs: ["props"], outputs: ["onAction"] }, { kind: "component", type: PermissionsModalComponent, selector: "val-permissions-modal", inputs: ["isOpen", "config"], outputs: ["dismissed"] }] }); }
|
|
51843
52622
|
}
|
|
51844
52623
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: OrganizationViewComponent, decorators: [{
|
|
51845
52624
|
type: Component,
|
|
@@ -51852,6 +52631,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
51852
52631
|
TextComponent,
|
|
51853
52632
|
ButtonComponent,
|
|
51854
52633
|
MemberCardComponent,
|
|
52634
|
+
PermissionsModalComponent,
|
|
51855
52635
|
TransferOwnershipModalComponent,
|
|
51856
52636
|
MemberImportModalComponent,
|
|
51857
52637
|
OrgInfoSheetComponent,
|
|
@@ -52142,6 +52922,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
52142
52922
|
</div>
|
|
52143
52923
|
</section>
|
|
52144
52924
|
}
|
|
52925
|
+
<val-permissions-modal
|
|
52926
|
+
[isOpen]="permissionsOpen()"
|
|
52927
|
+
[config]="{ i18nNamespace: resolvedConfig().i18nNamespace }"
|
|
52928
|
+
(dismissed)="permissionsOpen.set(false)"
|
|
52929
|
+
/>
|
|
52145
52930
|
</div>
|
|
52146
52931
|
`, styles: [".page{padding:16px 0;max-width:720px;margin:0 auto}.org-more-info-link{background:none;border:none;padding:4px 0;margin-top:4px;font-size:13px;font-weight:600;color:var(--ion-color-primary, #7026df);cursor:pointer;text-align:left}.page-header{margin-bottom:16px}.settings-section{padding:16px 0}.settings-section+.settings-section{border-top:1px solid var(--val-border-color, rgba(0, 0, 0, .08))}.section-header-row{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:8px}.section-body{display:flex;flex-direction:column;gap:10px}.row-actions{margin-top:12px}.row-actions--gap{display:flex;gap:8px}.invite-cta{display:flex;flex-direction:column;gap:16px}.invite-cta__text{display:flex;flex-direction:column;gap:4px}.invite-cta__actions{display:flex;gap:12px;flex-wrap:wrap}.org-info-card{border-radius:14px;background:var(--ion-color-light, #f4f5f8);overflow:hidden}:host-context(body.dark) .org-info-card,:host-context(html.ion-palette-dark) .org-info-card,:host-context([data-theme=\"dark\"]) .org-info-card{background:#ffffff0d}.org-info-field{padding:12px 16px;display:flex;flex-direction:column;gap:4px;border-bottom:1px solid var(--val-border-color, rgba(0, 0, 0, .06))}.org-info-field:last-child{border-bottom:none}.org-info-label{font-size:.74rem;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:var(--ion-color-medium)}.org-info-value{font-size:.95rem;font-weight:500;color:var(--ion-color-dark)}.org-info-value--muted{color:var(--ion-color-medium);font-weight:400}.plan-badge{display:inline-block;font-size:.78rem;font-weight:700;padding:3px 10px;border-radius:20px;width:fit-content}.plan-badge--free{background:var(--ion-color-light-shade, #d7d8da);color:var(--ion-color-dark)}.plan-badge--pro{background:var(--ion-color-primary);color:#fff}.plan-badge--enterprise{background:#f5c542;color:#222}.members-list{display:flex;flex-direction:column;gap:8px}.members-show-more{background:none;border:none;color:var(--ion-color-primary);font-size:14px;font-weight:500;cursor:pointer;padding:8px 0}.rbac-debug{opacity:.7}.rbac-debug__body{display:flex;flex-direction:column;gap:6px;font-family:monospace;font-size:.78rem}.rbac-debug__row{display:flex;gap:8px;align-items:flex-start}.rbac-debug__label{color:var(--ion-color-medium);min-width:72px;flex-shrink:0}.rbac-debug__value{color:var(--ion-color-dark);word-break:break-all}.rbac-debug__value--perms{color:var(--ion-color-medium)}\n"] }]
|
|
52147
52932
|
}], ctorParameters: () => [], propDecorators: { config: [{
|
|
@@ -52257,7 +53042,7 @@ addIcons({
|
|
|
52257
53042
|
notificationsOutline,
|
|
52258
53043
|
refreshOutline,
|
|
52259
53044
|
});
|
|
52260
|
-
const DEFAULT_NAMESPACE$
|
|
53045
|
+
const DEFAULT_NAMESPACE$5 = 'Settings.Notifications';
|
|
52261
53046
|
/**
|
|
52262
53047
|
* `val-notification-preferences-view` — **preferencias de notificaciones push**
|
|
52263
53048
|
* (FCM) full-feature autocontenida (organism). Promovida desde `showcase` bajo el
|
|
@@ -52300,7 +53085,7 @@ class NotificationPreferencesViewComponent {
|
|
|
52300
53085
|
const merged = { ...fromRoute, ...(this.config ?? {}) };
|
|
52301
53086
|
return {
|
|
52302
53087
|
showDetails: merged.showDetails ?? true,
|
|
52303
|
-
i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$
|
|
53088
|
+
i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$5,
|
|
52304
53089
|
onPushEnabled: merged.onPushEnabled,
|
|
52305
53090
|
onPushDisabled: merged.onPushDisabled,
|
|
52306
53091
|
};
|
|
@@ -52924,622 +53709,6 @@ function provideValtechNotificationPreferencesRoutes(opts) {
|
|
|
52924
53709
|
];
|
|
52925
53710
|
}
|
|
52926
53711
|
|
|
52927
|
-
/**
|
|
52928
|
-
* Defaults i18n (es/en) embebidos en `val-permissions-view`. Auto-registrados en
|
|
52929
|
-
* el constructor del componente si el consumer no proveyó el namespace
|
|
52930
|
-
* (`Settings.Permissions` por default). SOLO incluye las keys que la vista usa.
|
|
52931
|
-
*
|
|
52932
|
-
* Portado EXACTO de `PERMISSIONS_I18N` (showcase) bajo el proceso de ADR-021.
|
|
52933
|
-
*/
|
|
52934
|
-
const PERMISSIONS_VIEW_I18N = {
|
|
52935
|
-
es: {
|
|
52936
|
-
pageTitle: 'Permisos',
|
|
52937
|
-
pageDescription: 'Roles y permisos de esta app en la organización activa',
|
|
52938
|
-
systemRolesTitle: 'Roles del sistema',
|
|
52939
|
-
systemRolesHint: 'Predefinidos por la plataforma. No editables.',
|
|
52940
|
-
customRolesTitle: 'Roles personalizados',
|
|
52941
|
-
customRolesHint: 'Definidos por tu organización.',
|
|
52942
|
-
systemBadge: 'Sistema',
|
|
52943
|
-
noCustomRoles: 'Esta organización no tiene roles personalizados.',
|
|
52944
|
-
noPermissions: 'Sin permisos asignados.',
|
|
52945
|
-
// Grupos de alcance
|
|
52946
|
-
groupApp: 'Permisos de la app',
|
|
52947
|
-
groupOrg: 'Permisos de la organización',
|
|
52948
|
-
groupPlatform: 'Permisos de plataforma',
|
|
52949
|
-
groupOther: 'Otras apps',
|
|
52950
|
-
// Roles del sistema
|
|
52951
|
-
roleViewer: 'Visor',
|
|
52952
|
-
roleEditor: 'Editor',
|
|
52953
|
-
roleAdmin: 'Administrador',
|
|
52954
|
-
roleSuperAdmin: 'Super Administrador',
|
|
52955
|
-
roleBingoAdmin: 'Bingo · Administrador',
|
|
52956
|
-
roleBingoTesorero: 'Bingo · Tesorero',
|
|
52957
|
-
roleBingoTaquilla: 'Bingo · Taquilla',
|
|
52958
|
-
roleBingoOperator: 'Bingo · Operador',
|
|
52959
|
-
// Apps
|
|
52960
|
-
app_showcase: 'Showcase',
|
|
52961
|
-
app_bingo: 'Bingo',
|
|
52962
|
-
// Recursos
|
|
52963
|
-
permUsers: 'Usuarios',
|
|
52964
|
-
permRoles: 'Roles',
|
|
52965
|
-
permRbac: 'Control de acceso',
|
|
52966
|
-
permApps: 'Apps',
|
|
52967
|
-
'permApi-keys': 'API keys',
|
|
52968
|
-
'permEmail-logs': 'Logs de correo',
|
|
52969
|
-
'permSms-logs': 'Logs de SMS',
|
|
52970
|
-
permDocuments: 'Documentos',
|
|
52971
|
-
permMedia: 'Media',
|
|
52972
|
-
permTemplates: 'Plantillas',
|
|
52973
|
-
permRequests: 'Solicitudes',
|
|
52974
|
-
permBingo: 'Bingo',
|
|
52975
|
-
// Acciones
|
|
52976
|
-
permRead: 'Leer',
|
|
52977
|
-
permWrite: 'Escribir',
|
|
52978
|
-
permCreate: 'Crear',
|
|
52979
|
-
permDelete: 'Eliminar',
|
|
52980
|
-
permManage: 'Gestionar',
|
|
52981
|
-
permEdit: 'Editar',
|
|
52982
|
-
permSell: 'Vender',
|
|
52983
|
-
permCheckin: 'Canjear',
|
|
52984
|
-
permSettle: 'Rendir',
|
|
52985
|
-
permExport: 'Exportar',
|
|
52986
|
-
permOperate: 'Operar',
|
|
52987
|
-
permAll: 'Todo',
|
|
52988
|
-
// Loading / error
|
|
52989
|
-
errorTitle: 'Error al cargar permisos',
|
|
52990
|
-
retry: 'Reintentar',
|
|
52991
|
-
},
|
|
52992
|
-
en: {
|
|
52993
|
-
pageTitle: 'Permissions',
|
|
52994
|
-
pageDescription: 'Roles and permissions of this app in the active organization',
|
|
52995
|
-
systemRolesTitle: 'System roles',
|
|
52996
|
-
systemRolesHint: 'Defined by the platform. Not editable.',
|
|
52997
|
-
customRolesTitle: 'Custom roles',
|
|
52998
|
-
customRolesHint: 'Defined by your organization.',
|
|
52999
|
-
systemBadge: 'System',
|
|
53000
|
-
noCustomRoles: 'This organization has no custom roles.',
|
|
53001
|
-
noPermissions: 'No permissions assigned.',
|
|
53002
|
-
groupApp: 'App permissions',
|
|
53003
|
-
groupOrg: 'Organization permissions',
|
|
53004
|
-
groupPlatform: 'Platform permissions',
|
|
53005
|
-
groupOther: 'Other apps',
|
|
53006
|
-
roleViewer: 'Viewer',
|
|
53007
|
-
roleEditor: 'Editor',
|
|
53008
|
-
roleAdmin: 'Administrator',
|
|
53009
|
-
roleSuperAdmin: 'Super Administrator',
|
|
53010
|
-
roleBingoAdmin: 'Bingo · Administrator',
|
|
53011
|
-
roleBingoTesorero: 'Bingo · Treasurer',
|
|
53012
|
-
roleBingoTaquilla: 'Bingo · Box office',
|
|
53013
|
-
roleBingoOperator: 'Bingo · Operator',
|
|
53014
|
-
app_showcase: 'Showcase',
|
|
53015
|
-
app_bingo: 'Bingo',
|
|
53016
|
-
permUsers: 'Users',
|
|
53017
|
-
permRoles: 'Roles',
|
|
53018
|
-
permRbac: 'Access control',
|
|
53019
|
-
permApps: 'Apps',
|
|
53020
|
-
'permApi-keys': 'API keys',
|
|
53021
|
-
'permEmail-logs': 'Email logs',
|
|
53022
|
-
'permSms-logs': 'SMS logs',
|
|
53023
|
-
permDocuments: 'Documents',
|
|
53024
|
-
permMedia: 'Media',
|
|
53025
|
-
permTemplates: 'Templates',
|
|
53026
|
-
permRequests: 'Requests',
|
|
53027
|
-
permBingo: 'Bingo',
|
|
53028
|
-
permRead: 'Read',
|
|
53029
|
-
permWrite: 'Write',
|
|
53030
|
-
permCreate: 'Create',
|
|
53031
|
-
permDelete: 'Delete',
|
|
53032
|
-
permManage: 'Manage',
|
|
53033
|
-
permEdit: 'Edit',
|
|
53034
|
-
permSell: 'Sell',
|
|
53035
|
-
permCheckin: 'Check-in',
|
|
53036
|
-
permSettle: 'Settle',
|
|
53037
|
-
permExport: 'Export',
|
|
53038
|
-
permOperate: 'Operate',
|
|
53039
|
-
permAll: 'All',
|
|
53040
|
-
errorTitle: 'Error loading permissions',
|
|
53041
|
-
retry: 'Retry',
|
|
53042
|
-
},
|
|
53043
|
-
};
|
|
53044
|
-
|
|
53045
|
-
const DEFAULT_NAMESPACE$5 = 'Settings.Permissions';
|
|
53046
|
-
/**
|
|
53047
|
-
* `val-permissions-view` — vista Permisos full-feature autocontenida (organism).
|
|
53048
|
-
* Visor RBAC de solo lectura: muestra los roles del sistema (Visor/Editor/Admin +
|
|
53049
|
-
* roles de plataforma) y los roles personalizados de la org activa, con el
|
|
53050
|
-
* catálogo de permisos agrupado por alcance (app actual / plataforma / otras apps).
|
|
53051
|
-
* Promovida desde `showcase` bajo el proceso de ADR-021.
|
|
53052
|
-
*
|
|
53053
|
-
* **RBAC interno** (como `organization-view`): lee la org activa vía
|
|
53054
|
-
* `AuthService.user().activeOrgId`. La vista NO muta nada — el control de quién
|
|
53055
|
-
* puede verla es responsabilidad del guard de ruta del consumer; no hay gating
|
|
53056
|
-
* fino interno ni se expone como config.
|
|
53057
|
-
*
|
|
53058
|
-
* Datos: `forkJoin` de `OrgService.listOrgRoles(orgId)` (roles de la org) +
|
|
53059
|
-
* `PermissionCatalogService.getCatalog(orgId)` (catálogo + appId actual). El
|
|
53060
|
-
* catálogo define el alcance (`scope`/`appId`) de cada recurso para agrupar.
|
|
53061
|
-
*
|
|
53062
|
-
* NO renderiza ion-content — vive dentro de val-page-wrapper. `ActivatedRoute` se
|
|
53063
|
-
* inyecta `{ optional: true }` solo para leer el route data de config. Auto-registra
|
|
53064
|
-
* sus defaults i18n (es/en) si el consumer no proveyó el namespace (default
|
|
53065
|
-
* `Settings.Permissions`).
|
|
53066
|
-
*/
|
|
53067
|
-
class PermissionsViewComponent {
|
|
53068
|
-
/** Namespace i18n resuelto (capturado para llamadas no-reactivas). */
|
|
53069
|
-
get ns() {
|
|
53070
|
-
return this.resolvedConfig().i18nNamespace;
|
|
53071
|
-
}
|
|
53072
|
-
constructor() {
|
|
53073
|
-
this.i18n = inject(I18nService);
|
|
53074
|
-
this.orgService = inject(OrgService);
|
|
53075
|
-
this.catalog = inject(PermissionCatalogService);
|
|
53076
|
-
this.auth = inject(AuthService);
|
|
53077
|
-
this.nav = inject(NavigationService);
|
|
53078
|
-
this.route = inject(ActivatedRoute, { optional: true });
|
|
53079
|
-
this.resolvedConfig = computed(() => {
|
|
53080
|
-
const fromRoute = (this.route?.snapshot.data['permissionsConfig'] ?? {});
|
|
53081
|
-
const merged = { ...fromRoute, ...(this.config ?? {}) };
|
|
53082
|
-
return {
|
|
53083
|
-
showSystemRoles: merged.showSystemRoles ?? true,
|
|
53084
|
-
showCustomRoles: merged.showCustomRoles ?? true,
|
|
53085
|
-
i18nNamespace: merged.i18nNamespace ?? DEFAULT_NAMESPACE$5,
|
|
53086
|
-
};
|
|
53087
|
-
});
|
|
53088
|
-
this.loading = signal(false);
|
|
53089
|
-
this.loadError = signal(null);
|
|
53090
|
-
this.roles = signal([]);
|
|
53091
|
-
this.systemRoles = computed(() => this.roles().filter(r => r.isSystem));
|
|
53092
|
-
this.customRoles = computed(() => this.roles().filter(r => !r.isSystem));
|
|
53093
|
-
this.activeOrgId = computed(() => {
|
|
53094
|
-
const u = this.auth.user();
|
|
53095
|
-
return u?.activeOrgId ?? u?.activeOrg ?? '';
|
|
53096
|
-
});
|
|
53097
|
-
this.retryButtonProps = computed(() => ({
|
|
53098
|
-
text: this.tt('retry'),
|
|
53099
|
-
color: 'dark',
|
|
53100
|
-
fill: 'outline',
|
|
53101
|
-
size: 'default',
|
|
53102
|
-
type: 'button',
|
|
53103
|
-
state: this.loading() ? ComponentStates.DISABLED : ComponentStates.ENABLED,
|
|
53104
|
-
}));
|
|
53105
|
-
const ns = this.ns;
|
|
53106
|
-
if (!this.i18n.hasNamespace(ns)) {
|
|
53107
|
-
this.i18n.registerContent(ns, PERMISSIONS_VIEW_I18N);
|
|
53108
|
-
}
|
|
53109
|
-
this.nav.setBackHeader('pageTitle', ns, { withMenu: true });
|
|
53110
|
-
this.load();
|
|
53111
|
-
connectPageRefresh(() => this.load());
|
|
53112
|
-
}
|
|
53113
|
-
load() {
|
|
53114
|
-
const orgId = this.activeOrgId();
|
|
53115
|
-
if (!orgId)
|
|
53116
|
-
return;
|
|
53117
|
-
this.loading.set(true);
|
|
53118
|
-
this.loadError.set(null);
|
|
53119
|
-
forkJoin({
|
|
53120
|
-
roles: this.orgService.listOrgRoles(orgId),
|
|
53121
|
-
catalog: this.catalog.getCatalog(orgId),
|
|
53122
|
-
}).subscribe({
|
|
53123
|
-
next: ({ roles: rawRoles, catalog }) => {
|
|
53124
|
-
const byResource = new Map();
|
|
53125
|
-
for (const r of catalog.permissions)
|
|
53126
|
-
byResource.set(r.resource, r);
|
|
53127
|
-
const currentAppId = catalog.appId;
|
|
53128
|
-
// Labeler con la SSOT del backend (labels del catálogo) — reemplaza el
|
|
53129
|
-
// mapa hardcodeado `perm*`. Locale del momento de carga (igual que antes:
|
|
53130
|
-
// los labels se hornean en el RoleView; cambio de idioma requiere reload).
|
|
53131
|
-
const labeler = createPermissionLabeler(catalog, this.i18n.lang(), {
|
|
53132
|
-
allLabel: this.tt('permAll'),
|
|
53133
|
-
});
|
|
53134
|
-
this.roles.set((rawRoles ?? []).map((r) => this.buildRoleView({
|
|
53135
|
-
id: r.id,
|
|
53136
|
-
name: r.name,
|
|
53137
|
-
description: r.description,
|
|
53138
|
-
permissions: r.permissions ?? [],
|
|
53139
|
-
isSystem: r.isSystem ?? false,
|
|
53140
|
-
}, byResource, currentAppId, labeler)));
|
|
53141
|
-
this.loading.set(false);
|
|
53142
|
-
},
|
|
53143
|
-
error: err => {
|
|
53144
|
-
this.loadError.set(err);
|
|
53145
|
-
this.loading.set(false);
|
|
53146
|
-
},
|
|
53147
|
-
});
|
|
53148
|
-
}
|
|
53149
|
-
buildRoleView(role, byResource, currentAppId, labeler) {
|
|
53150
|
-
const appPerms = [];
|
|
53151
|
-
const orgPerms = [];
|
|
53152
|
-
const otherByApp = new Map();
|
|
53153
|
-
for (const perm of role.permissions) {
|
|
53154
|
-
const [resource] = perm.split(':');
|
|
53155
|
-
const entry = byResource.get(resource);
|
|
53156
|
-
// Sin entrada en el catálogo (el backend omite los recursos internal para
|
|
53157
|
-
// no-platform-admins) o entrada internal → NUNCA se muestra a nivel de
|
|
53158
|
-
// producto. Defensa en profundidad junto al filtro del backend (ADR-024).
|
|
53159
|
-
// Nota: el rol puede traer el permiso aunque el recurso esté filtrado del
|
|
53160
|
-
// catálogo (ej. admin tiene templates:*); por eso se descarta lo no resuelto.
|
|
53161
|
-
if (!entry || entry.scope === 'internal') {
|
|
53162
|
-
continue;
|
|
53163
|
-
}
|
|
53164
|
-
const label = labeler(perm);
|
|
53165
|
-
if (entry.scope === 'app') {
|
|
53166
|
-
if (entry.appId === currentAppId) {
|
|
53167
|
-
appPerms.push(label);
|
|
53168
|
-
}
|
|
53169
|
-
else {
|
|
53170
|
-
const appId = entry.appId ?? '?';
|
|
53171
|
-
const arr = otherByApp.get(appId) ?? [];
|
|
53172
|
-
arr.push(label);
|
|
53173
|
-
otherByApp.set(appId, arr);
|
|
53174
|
-
}
|
|
53175
|
-
}
|
|
53176
|
-
else {
|
|
53177
|
-
// scope === 'org' → gestión de la organización del cliente
|
|
53178
|
-
orgPerms.push(label);
|
|
53179
|
-
}
|
|
53180
|
-
}
|
|
53181
|
-
const groups = [];
|
|
53182
|
-
if (appPerms.length) {
|
|
53183
|
-
groups.push({ key: 'app', label: this.tt('groupApp'), perms: appPerms });
|
|
53184
|
-
}
|
|
53185
|
-
if (orgPerms.length) {
|
|
53186
|
-
groups.push({
|
|
53187
|
-
key: 'org',
|
|
53188
|
-
label: this.tt('groupOrg'),
|
|
53189
|
-
perms: orgPerms,
|
|
53190
|
-
});
|
|
53191
|
-
}
|
|
53192
|
-
for (const [appId, perms] of otherByApp) {
|
|
53193
|
-
groups.push({
|
|
53194
|
-
key: `other:${appId}`,
|
|
53195
|
-
label: `${this.tt('groupOther')}: ${this.appLabel(appId)}`,
|
|
53196
|
-
perms,
|
|
53197
|
-
});
|
|
53198
|
-
}
|
|
53199
|
-
return {
|
|
53200
|
-
id: role.id,
|
|
53201
|
-
name: role.name,
|
|
53202
|
-
description: role.description,
|
|
53203
|
-
isSystem: role.isSystem,
|
|
53204
|
-
groups,
|
|
53205
|
-
hasPerms: groups.length > 0,
|
|
53206
|
-
};
|
|
53207
|
-
}
|
|
53208
|
-
tt(key) {
|
|
53209
|
-
return this.i18n.t(key, this.ns);
|
|
53210
|
-
}
|
|
53211
|
-
appLabel(appId) {
|
|
53212
|
-
const key = `app_${appId}`;
|
|
53213
|
-
const label = this.tt(key);
|
|
53214
|
-
return label === key ? appId : label;
|
|
53215
|
-
}
|
|
53216
|
-
roleLabel(name) {
|
|
53217
|
-
const map = {
|
|
53218
|
-
viewer: 'roleViewer',
|
|
53219
|
-
editor: 'roleEditor',
|
|
53220
|
-
admin: 'roleAdmin',
|
|
53221
|
-
super_admin: 'roleSuperAdmin',
|
|
53222
|
-
bingo_admin: 'roleBingoAdmin',
|
|
53223
|
-
bingo_tesorero: 'roleBingoTesorero',
|
|
53224
|
-
bingo_taquilla: 'roleBingoTaquilla',
|
|
53225
|
-
bingo_operator: 'roleBingoOperator',
|
|
53226
|
-
};
|
|
53227
|
-
return this.tt(map[name] ?? name);
|
|
53228
|
-
}
|
|
53229
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: PermissionsViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
53230
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: PermissionsViewComponent, isStandalone: true, selector: "val-permissions-view", inputs: { config: "config" }, ngImport: i0, template: `
|
|
53231
|
-
<div class="page">
|
|
53232
|
-
<header class="page-header">
|
|
53233
|
-
<val-display [props]="{ size: 'small', color: 'dark', content: tt('pageTitle') }" />
|
|
53234
|
-
<val-title
|
|
53235
|
-
[props]="{
|
|
53236
|
-
size: 'large',
|
|
53237
|
-
color: 'dark',
|
|
53238
|
-
bold: false,
|
|
53239
|
-
content: tt('pageDescription'),
|
|
53240
|
-
}"
|
|
53241
|
-
/>
|
|
53242
|
-
</header>
|
|
53243
|
-
|
|
53244
|
-
@if (loading()) {
|
|
53245
|
-
<div class="spinner-row">
|
|
53246
|
-
<ion-spinner name="crescent" />
|
|
53247
|
-
</div>
|
|
53248
|
-
} @else if (loadError()) {
|
|
53249
|
-
<div class="error-row">
|
|
53250
|
-
<val-text
|
|
53251
|
-
[props]="{
|
|
53252
|
-
content: tt('errorTitle'),
|
|
53253
|
-
size: 'medium',
|
|
53254
|
-
color: 'medium',
|
|
53255
|
-
bold: false,
|
|
53256
|
-
}"
|
|
53257
|
-
/>
|
|
53258
|
-
<val-button [props]="retryButtonProps()" (click)="load()" />
|
|
53259
|
-
</div>
|
|
53260
|
-
} @else {
|
|
53261
|
-
@if (resolvedConfig().showSystemRoles) {
|
|
53262
|
-
<section class="perm-section">
|
|
53263
|
-
<div class="perm-section__header">
|
|
53264
|
-
<val-title
|
|
53265
|
-
[props]="{
|
|
53266
|
-
size: 'medium',
|
|
53267
|
-
color: 'dark',
|
|
53268
|
-
bold: true,
|
|
53269
|
-
content: tt('systemRolesTitle'),
|
|
53270
|
-
}"
|
|
53271
|
-
/>
|
|
53272
|
-
<val-text
|
|
53273
|
-
[props]="{
|
|
53274
|
-
content: tt('systemRolesHint'),
|
|
53275
|
-
size: 'medium',
|
|
53276
|
-
color: 'medium',
|
|
53277
|
-
bold: false,
|
|
53278
|
-
}"
|
|
53279
|
-
/>
|
|
53280
|
-
</div>
|
|
53281
|
-
<div class="role-list">
|
|
53282
|
-
@for (role of systemRoles(); track role.id) {
|
|
53283
|
-
<div class="role-card">
|
|
53284
|
-
<div class="role-card__header">
|
|
53285
|
-
<span class="role-card__name">{{ roleLabel(role.name) }}</span>
|
|
53286
|
-
<val-pill
|
|
53287
|
-
[props]="{
|
|
53288
|
-
label: tt('systemBadge'),
|
|
53289
|
-
color: 'primary',
|
|
53290
|
-
weight: 'bold',
|
|
53291
|
-
}"
|
|
53292
|
-
/>
|
|
53293
|
-
</div>
|
|
53294
|
-
@if (role.description) {
|
|
53295
|
-
<span class="role-card__desc">{{ role.description }}</span>
|
|
53296
|
-
}
|
|
53297
|
-
@if (!role.hasPerms) {
|
|
53298
|
-
<div class="perm-chips">
|
|
53299
|
-
<span class="perm-chip perm-chip--empty">{{ tt('noPermissions') }}</span>
|
|
53300
|
-
</div>
|
|
53301
|
-
} @else {
|
|
53302
|
-
@for (g of role.groups; track g.key) {
|
|
53303
|
-
<div class="perm-group">
|
|
53304
|
-
<span class="perm-group__label">{{ g.label }}</span>
|
|
53305
|
-
<div class="perm-chips">
|
|
53306
|
-
@for (p of g.perms; track p) {
|
|
53307
|
-
<span class="perm-chip">{{ p }}</span>
|
|
53308
|
-
}
|
|
53309
|
-
</div>
|
|
53310
|
-
</div>
|
|
53311
|
-
}
|
|
53312
|
-
}
|
|
53313
|
-
</div>
|
|
53314
|
-
}
|
|
53315
|
-
</div>
|
|
53316
|
-
</section>
|
|
53317
|
-
}
|
|
53318
|
-
|
|
53319
|
-
@if (resolvedConfig().showCustomRoles) {
|
|
53320
|
-
<section class="perm-section">
|
|
53321
|
-
<div class="perm-section__header">
|
|
53322
|
-
<val-title
|
|
53323
|
-
[props]="{
|
|
53324
|
-
size: 'medium',
|
|
53325
|
-
color: 'dark',
|
|
53326
|
-
bold: true,
|
|
53327
|
-
content: tt('customRolesTitle'),
|
|
53328
|
-
}"
|
|
53329
|
-
/>
|
|
53330
|
-
<val-text
|
|
53331
|
-
[props]="{
|
|
53332
|
-
content: tt('customRolesHint'),
|
|
53333
|
-
size: 'medium',
|
|
53334
|
-
color: 'medium',
|
|
53335
|
-
bold: false,
|
|
53336
|
-
}"
|
|
53337
|
-
/>
|
|
53338
|
-
</div>
|
|
53339
|
-
@if (customRoles().length === 0) {
|
|
53340
|
-
<val-text
|
|
53341
|
-
[props]="{
|
|
53342
|
-
content: tt('noCustomRoles'),
|
|
53343
|
-
size: 'medium',
|
|
53344
|
-
color: 'medium',
|
|
53345
|
-
bold: false,
|
|
53346
|
-
}"
|
|
53347
|
-
/>
|
|
53348
|
-
} @else {
|
|
53349
|
-
<div class="role-list">
|
|
53350
|
-
@for (role of customRoles(); track role.id) {
|
|
53351
|
-
<div class="role-card">
|
|
53352
|
-
<div class="role-card__header">
|
|
53353
|
-
<span class="role-card__name">{{ role.name }}</span>
|
|
53354
|
-
</div>
|
|
53355
|
-
@if (role.description) {
|
|
53356
|
-
<span class="role-card__desc">{{ role.description }}</span>
|
|
53357
|
-
}
|
|
53358
|
-
@if (!role.hasPerms) {
|
|
53359
|
-
<div class="perm-chips">
|
|
53360
|
-
<span class="perm-chip perm-chip--empty">{{ tt('noPermissions') }}</span>
|
|
53361
|
-
</div>
|
|
53362
|
-
} @else {
|
|
53363
|
-
@for (g of role.groups; track g.key) {
|
|
53364
|
-
<div class="perm-group">
|
|
53365
|
-
<span class="perm-group__label">{{ g.label }}</span>
|
|
53366
|
-
<div class="perm-chips">
|
|
53367
|
-
@for (p of g.perms; track p) {
|
|
53368
|
-
<span class="perm-chip">{{ p }}</span>
|
|
53369
|
-
}
|
|
53370
|
-
</div>
|
|
53371
|
-
</div>
|
|
53372
|
-
}
|
|
53373
|
-
}
|
|
53374
|
-
</div>
|
|
53375
|
-
}
|
|
53376
|
-
</div>
|
|
53377
|
-
}
|
|
53378
|
-
</section>
|
|
53379
|
-
}
|
|
53380
|
-
}
|
|
53381
|
-
</div>
|
|
53382
|
-
`, isInline: true, styles: [".page{padding:16px 0;max-width:720px;margin:0 auto}.page-header{margin-bottom:24px}.spinner-row,.error-row{display:flex;flex-direction:column;align-items:center;gap:16px;padding:32px 0}.perm-section{margin-bottom:32px}.perm-section__header{margin-bottom:12px}.role-list{display:flex;flex-direction:column;gap:10px}.role-card{border:1.5px solid var(--ion-color-light);border-radius:12px;padding:14px 16px;display:flex;flex-direction:column;gap:10px}.role-card__header{display:flex;align-items:center;gap:8px;flex-wrap:wrap}.role-card__name{font-size:15px;font-weight:600;color:var(--ion-color-dark)}.role-card__desc{font-size:13px;color:var(--ion-color-medium);line-height:1.4}.perm-group{display:flex;flex-direction:column;gap:6px}.perm-group__label{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--ion-color-medium)}.perm-chips{display:flex;flex-wrap:wrap;gap:6px}.perm-chip{font-size:12px;padding:3px 10px;border-radius:20px;background:var(--ion-color-light);color:var(--ion-color-dark)}.perm-chip--empty{color:var(--ion-color-medium);background:transparent;padding-left:0}\n"], dependencies: [{ kind: "component", type: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { kind: "component", type: DisplayComponent, selector: "val-display", inputs: ["props"] }, { kind: "component", type: PillComponent, selector: "val-pill", inputs: ["preset", "props"], outputs: ["pillClick", "pillAction"] }, { kind: "component", type: TitleComponent, selector: "val-title", inputs: ["props"] }, { kind: "component", type: TextComponent, selector: "val-text", inputs: ["props"] }, { kind: "component", type: ButtonComponent, selector: "val-button", inputs: ["preset", "props"], outputs: ["onClick"] }] }); }
|
|
53383
|
-
}
|
|
53384
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: PermissionsViewComponent, decorators: [{
|
|
53385
|
-
type: Component,
|
|
53386
|
-
args: [{ selector: 'val-permissions-view', standalone: true, imports: [IonSpinner, DisplayComponent, PillComponent, TitleComponent, TextComponent, ButtonComponent], template: `
|
|
53387
|
-
<div class="page">
|
|
53388
|
-
<header class="page-header">
|
|
53389
|
-
<val-display [props]="{ size: 'small', color: 'dark', content: tt('pageTitle') }" />
|
|
53390
|
-
<val-title
|
|
53391
|
-
[props]="{
|
|
53392
|
-
size: 'large',
|
|
53393
|
-
color: 'dark',
|
|
53394
|
-
bold: false,
|
|
53395
|
-
content: tt('pageDescription'),
|
|
53396
|
-
}"
|
|
53397
|
-
/>
|
|
53398
|
-
</header>
|
|
53399
|
-
|
|
53400
|
-
@if (loading()) {
|
|
53401
|
-
<div class="spinner-row">
|
|
53402
|
-
<ion-spinner name="crescent" />
|
|
53403
|
-
</div>
|
|
53404
|
-
} @else if (loadError()) {
|
|
53405
|
-
<div class="error-row">
|
|
53406
|
-
<val-text
|
|
53407
|
-
[props]="{
|
|
53408
|
-
content: tt('errorTitle'),
|
|
53409
|
-
size: 'medium',
|
|
53410
|
-
color: 'medium',
|
|
53411
|
-
bold: false,
|
|
53412
|
-
}"
|
|
53413
|
-
/>
|
|
53414
|
-
<val-button [props]="retryButtonProps()" (click)="load()" />
|
|
53415
|
-
</div>
|
|
53416
|
-
} @else {
|
|
53417
|
-
@if (resolvedConfig().showSystemRoles) {
|
|
53418
|
-
<section class="perm-section">
|
|
53419
|
-
<div class="perm-section__header">
|
|
53420
|
-
<val-title
|
|
53421
|
-
[props]="{
|
|
53422
|
-
size: 'medium',
|
|
53423
|
-
color: 'dark',
|
|
53424
|
-
bold: true,
|
|
53425
|
-
content: tt('systemRolesTitle'),
|
|
53426
|
-
}"
|
|
53427
|
-
/>
|
|
53428
|
-
<val-text
|
|
53429
|
-
[props]="{
|
|
53430
|
-
content: tt('systemRolesHint'),
|
|
53431
|
-
size: 'medium',
|
|
53432
|
-
color: 'medium',
|
|
53433
|
-
bold: false,
|
|
53434
|
-
}"
|
|
53435
|
-
/>
|
|
53436
|
-
</div>
|
|
53437
|
-
<div class="role-list">
|
|
53438
|
-
@for (role of systemRoles(); track role.id) {
|
|
53439
|
-
<div class="role-card">
|
|
53440
|
-
<div class="role-card__header">
|
|
53441
|
-
<span class="role-card__name">{{ roleLabel(role.name) }}</span>
|
|
53442
|
-
<val-pill
|
|
53443
|
-
[props]="{
|
|
53444
|
-
label: tt('systemBadge'),
|
|
53445
|
-
color: 'primary',
|
|
53446
|
-
weight: 'bold',
|
|
53447
|
-
}"
|
|
53448
|
-
/>
|
|
53449
|
-
</div>
|
|
53450
|
-
@if (role.description) {
|
|
53451
|
-
<span class="role-card__desc">{{ role.description }}</span>
|
|
53452
|
-
}
|
|
53453
|
-
@if (!role.hasPerms) {
|
|
53454
|
-
<div class="perm-chips">
|
|
53455
|
-
<span class="perm-chip perm-chip--empty">{{ tt('noPermissions') }}</span>
|
|
53456
|
-
</div>
|
|
53457
|
-
} @else {
|
|
53458
|
-
@for (g of role.groups; track g.key) {
|
|
53459
|
-
<div class="perm-group">
|
|
53460
|
-
<span class="perm-group__label">{{ g.label }}</span>
|
|
53461
|
-
<div class="perm-chips">
|
|
53462
|
-
@for (p of g.perms; track p) {
|
|
53463
|
-
<span class="perm-chip">{{ p }}</span>
|
|
53464
|
-
}
|
|
53465
|
-
</div>
|
|
53466
|
-
</div>
|
|
53467
|
-
}
|
|
53468
|
-
}
|
|
53469
|
-
</div>
|
|
53470
|
-
}
|
|
53471
|
-
</div>
|
|
53472
|
-
</section>
|
|
53473
|
-
}
|
|
53474
|
-
|
|
53475
|
-
@if (resolvedConfig().showCustomRoles) {
|
|
53476
|
-
<section class="perm-section">
|
|
53477
|
-
<div class="perm-section__header">
|
|
53478
|
-
<val-title
|
|
53479
|
-
[props]="{
|
|
53480
|
-
size: 'medium',
|
|
53481
|
-
color: 'dark',
|
|
53482
|
-
bold: true,
|
|
53483
|
-
content: tt('customRolesTitle'),
|
|
53484
|
-
}"
|
|
53485
|
-
/>
|
|
53486
|
-
<val-text
|
|
53487
|
-
[props]="{
|
|
53488
|
-
content: tt('customRolesHint'),
|
|
53489
|
-
size: 'medium',
|
|
53490
|
-
color: 'medium',
|
|
53491
|
-
bold: false,
|
|
53492
|
-
}"
|
|
53493
|
-
/>
|
|
53494
|
-
</div>
|
|
53495
|
-
@if (customRoles().length === 0) {
|
|
53496
|
-
<val-text
|
|
53497
|
-
[props]="{
|
|
53498
|
-
content: tt('noCustomRoles'),
|
|
53499
|
-
size: 'medium',
|
|
53500
|
-
color: 'medium',
|
|
53501
|
-
bold: false,
|
|
53502
|
-
}"
|
|
53503
|
-
/>
|
|
53504
|
-
} @else {
|
|
53505
|
-
<div class="role-list">
|
|
53506
|
-
@for (role of customRoles(); track role.id) {
|
|
53507
|
-
<div class="role-card">
|
|
53508
|
-
<div class="role-card__header">
|
|
53509
|
-
<span class="role-card__name">{{ role.name }}</span>
|
|
53510
|
-
</div>
|
|
53511
|
-
@if (role.description) {
|
|
53512
|
-
<span class="role-card__desc">{{ role.description }}</span>
|
|
53513
|
-
}
|
|
53514
|
-
@if (!role.hasPerms) {
|
|
53515
|
-
<div class="perm-chips">
|
|
53516
|
-
<span class="perm-chip perm-chip--empty">{{ tt('noPermissions') }}</span>
|
|
53517
|
-
</div>
|
|
53518
|
-
} @else {
|
|
53519
|
-
@for (g of role.groups; track g.key) {
|
|
53520
|
-
<div class="perm-group">
|
|
53521
|
-
<span class="perm-group__label">{{ g.label }}</span>
|
|
53522
|
-
<div class="perm-chips">
|
|
53523
|
-
@for (p of g.perms; track p) {
|
|
53524
|
-
<span class="perm-chip">{{ p }}</span>
|
|
53525
|
-
}
|
|
53526
|
-
</div>
|
|
53527
|
-
</div>
|
|
53528
|
-
}
|
|
53529
|
-
}
|
|
53530
|
-
</div>
|
|
53531
|
-
}
|
|
53532
|
-
</div>
|
|
53533
|
-
}
|
|
53534
|
-
</section>
|
|
53535
|
-
}
|
|
53536
|
-
}
|
|
53537
|
-
</div>
|
|
53538
|
-
`, styles: [".page{padding:16px 0;max-width:720px;margin:0 auto}.page-header{margin-bottom:24px}.spinner-row,.error-row{display:flex;flex-direction:column;align-items:center;gap:16px;padding:32px 0}.perm-section{margin-bottom:32px}.perm-section__header{margin-bottom:12px}.role-list{display:flex;flex-direction:column;gap:10px}.role-card{border:1.5px solid var(--ion-color-light);border-radius:12px;padding:14px 16px;display:flex;flex-direction:column;gap:10px}.role-card__header{display:flex;align-items:center;gap:8px;flex-wrap:wrap}.role-card__name{font-size:15px;font-weight:600;color:var(--ion-color-dark)}.role-card__desc{font-size:13px;color:var(--ion-color-medium);line-height:1.4}.perm-group{display:flex;flex-direction:column;gap:6px}.perm-group__label{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--ion-color-medium)}.perm-chips{display:flex;flex-wrap:wrap;gap:6px}.perm-chip{font-size:12px;padding:3px 10px;border-radius:20px;background:var(--ion-color-light);color:var(--ion-color-dark)}.perm-chip--empty{color:var(--ion-color-medium);background:transparent;padding-left:0}\n"] }]
|
|
53539
|
-
}], ctorParameters: () => [], propDecorators: { config: [{
|
|
53540
|
-
type: Input
|
|
53541
|
-
}] } });
|
|
53542
|
-
|
|
53543
53712
|
/**
|
|
53544
53713
|
* Helper para montar la vista Permisos (`val-permissions-view`) como ruta en una
|
|
53545
53714
|
* app del factory. El `config` se pasa por route `data` (`permissionsConfig`) y el
|
|
@@ -67204,5 +67373,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
67204
67373
|
* Generated bundle index. Do not edit.
|
|
67205
67374
|
*/
|
|
67206
67375
|
|
|
67207
|
-
export { ACTION_CARD_DEFAULTS, AD_SIZE_MAP, API_TABLE_COLUMN_LABELS, APP_VERSION_PLATFORM_PLUGIN, APP_VERSION_REMOTE_PLUGIN, ARTICLE_CARD_DEFAULTS, ARTICLE_SPACING, ARTICLE_STRIP_DEFAULTS, AUTH_CTA_DEFAULTS, AVATAR_UPLOAD_DEFAULTS, AboutViewComponent, AccordionComponent, AccountViewComponent, ActionCardComponent, ActionHeaderComponent, ActionType, AdSlotComponent, AdsLoaderService, AdsService, AlertBoxComponent, AnalyticsErrorHandler, AnalyticsRouterTracker, AnalyticsService, AnimatedTerminalComponent, ApiKeyCreateModalComponent, ApiKeyService, ApiKeysModalComponent, ApiKeysViewComponent, AppConfigService, AppVersionService, ArticleBuilder, ArticleCardComponent, ArticleComponent, ArticleStripComponent, AttachmentUploaderComponent, AuthBackgroundComponent, AuthCtaComponent, AuthService, AuthStateService, AuthStorageService, AuthSyncService, AvatarComponent, AvatarUploadComponent, BOTTOM_NAV_DEFAULTS, BannerComponent, BlogPostBuilder, BottomNavComponent, BoxComponent, BreadcrumbComponent, ButtonComponent, ButtonGroupComponent, CALLOUT_LABELS, CHEV_KEYS, CIRCLE_KEYS, COMMON_COUNTRY_CODES, COMMON_CURRENCIES, CORNER_KEYS, CTA_CARD_DEFAULTS, CURRENCY_INFO, CardComponent, CardSection, CardType, CardsCarouselComponent, ChangeEmailModalComponent, ChangePasswordModalComponent, CheckInputComponent, CheckboxRadioInputComponent, ChipGroupComponent, CodeDisplayComponent, CommandDisplayComponent, CommentComponent, CommentInputComponent, CommentSectionComponent, CompanyFooterComponent, ComponentStates, ConfirmationDialogService, ContainerComponent, ContentLoaderComponent, ContentReactionComponent, ContentService, ContentTransformer, CookieBannerComponent, CountdownComponent, CreateOrgModalComponent, CtaCardComponent, CurrencyInputComponent, DEFAULT_ADS_CONFIG, DEFAULT_APP_CONFIG_SERVICE_CONFIG, DEFAULT_APP_VERSION_SERVICE_CONFIG, DEFAULT_AUTH_CONFIG, DEFAULT_BACK_HEADER, DEFAULT_BUTTON_PRESETS, DEFAULT_CANCEL_BUTTON, DEFAULT_CHECK_INTERVAL_MS, DEFAULT_CONFIRM_BUTTON, DEFAULT_COUNTDOWN_LABELS, DEFAULT_COUNTDOWN_LABELS_EN, DEFAULT_DEBUG_CONSOLE_CONFIG, DEFAULT_DONATION_CONFIG, DEFAULT_EMPTY_STATE, DEFAULT_EMULATOR_CONFIG, DEFAULT_FEEDBACK_CONFIG, DEFAULT_FEEDBACK_TYPE_OPTIONS, DEFAULT_HOME_HEADER, DEFAULT_INFINITE_LIST_METADATA, DEFAULT_MODAL_CANCEL_BUTTON, DEFAULT_MODAL_CONFIRM_BUTTON, DEFAULT_PAGE_SIZE_OPTIONS, DEFAULT_PLATFORMS, DEFAULT_PRESETS, DEFAULT_REFRESHER_METADATA, DEFAULT_SKELETON_CONFIG, DEFAULT_SPLASH_SCREEN_CONFIG, DataTableComponent, DateInputComponent, DatePickerComponent, DateRangeInputComponent, DebugConsoleComponent, DeleteAccountModalComponent, DetailSkeletonComponent, DeviceService, DisplayComponent, DividerComponent, DocsApiTableComponent, DocsBreadcrumbComponent, DocsBuilder, DocsCalloutComponent, DocsCodeExampleComponent, DocsLayoutComponent, DocsNavLinksComponent, DocsNavigationService, DocsPageComponent, DocsSearchComponent, DocsSectionComponent, DocsShellComponent, DocsSidebarComponent, DocsTocComponent, DonationService, DownloadService, EditOrgModalComponent, EmptyStateComponent, ExpandableTextComponent, FEATURES_LIST_DEFAULTS, FUN_MODAL_DEFAULTS, FabComponent, FaqComponent, FeaturesListComponent, FeedbackFormComponent, FeedbackService, FieldListComponent, FileInputComponent, FirebaseService, FirestoreCollectionFactory, FirestoreService, FontSizeOption, FontSizeService, FooterComponent, FooterLinksComponent, FormComponent, FormSkeletonComponent, FunHeaderComponent, FunModalComponent, GlassComponent, GlowCardComponent, GlowComponent, GridSkeletonComponent, HANDOFF_ROUTE_PARAM, HANDOFF_TOKEN_PARAM, HandoffService, HasPermissionDirective, HeaderComponent, HintComponent, HorizontalScrollComponent, HrefComponent, I18nService, IMAGE_DEFAULTS, INITIAL_AUTH_STATE, INITIAL_MFA_STATE, INVITATION_CARD_DEFAULTS, IONIC_COLORS$5 as IONIC_COLORS, Icon, IconComponent, IconService, ImageComponent, ImageCropComponent, ImageService, InAppBrowserService, InfiniteListComponent, InfoComponent, InputI18nHelper, InputType, InvitationCardComponent, InviteMemberModalComponent, ItemListComponent, LANG_STORAGE_KEY$1 as LANG_STORAGE_KEY, LEGAL_CONTENT_CONFIG, LOGIN_DEFAULTS, LandingSplitComponent, LandingStepsComponent, LanguageSelectorComponent, LayeredCardComponent, LegalContentService, LegalLinkService, LinkComponent, LinkProcessorService, LinkedProvidersComponent, LinksAccordionComponent, LinksCakeComponent, ListSkeletonComponent, LoadingDirective, LocalStorageService, LocaleService, LoginAttemptModalComponent, LoginComponent, MEDIA_OBJECT_DEFAULTS, MEMBER_CARD_DEFAULTS, METRIC_CARD_DEFAULTS, MODAL_SIZES, MOTIF_KEYS, MOTION, MaintenancePageComponent, MarkdownArticleParserService, MediaObjectComponent, MemberCardComponent, MemberDetailModalComponent, MemberImportModalComponent, MenuComponent, MessagingService, MetaService, MetricCardComponent, MfaModalComponent, ModalService, ModalShellComponent, MultiSelectSearchComponent, NUM_KEYS, NavigationService, NetworkBannerComponent, NetworkStatusService, NewsBuilder, NoContentComponent, NotesBoxComponent, NotificationActionService, NotificationPreferencesViewComponent, NotificationsService, NotificationsViewComponent, NumberFromToComponent, NumberStepperComponent, OAUTH_PROVIDERS_INFO, OAuthCallbackComponent, OAuthService, OrgInfoSheetComponent, OrgService, OrgSwitchService, OrganizationViewComponent, PATTERN_MOTIFS, PATTERN_PALETTES, PATTERN_STYLE_CONFIGS, PLATFORM_CONFIGS, PageContentComponent, PageLinksComponent, PageRefreshService, PageTemplateComponent, PageWavesComponent, PageWrapperComponent, PaginationComponent, PaginationService, PasswordInputComponent, PatternComponent, PermissionCatalogService, PermissionSelectorComponent, PermissionsViewComponent, PhoneInputComponent, PillComponent, PinInputComponent, PlainCodeBoxComponent, PopoverSelectorComponent, PreferencesService, PreferencesViewComponent, PresetService, PriceTagComponent, ProcessLinksPipe, ProfileSkeletonComponent, ProfileViewComponent, ProgressBarComponent, ProgressRingComponent, ProgressStatusComponent, PrompterComponent, QR_PRESETS, QrCodeComponent, QrGeneratorService, QrScannerComponent, QueryBuilder, QuoteBoxComponent, REQUEST_STATUSES, RadioInputComponent, RangeInputComponent, RatingComponent, RefresherComponent, RequestFirestoreService, RequestFormBuilderService, RequestService, RightsFooterComponent, RotatingTextComponent, SEARCH_HEADER_DEFAULTS, SETTINGS_SECTIONS_CATALOG, SHAPE_KEYS, SHARE_PROFILE_MODAL_DEFAULTS, SKELETON_LAYOUT_DEFAULT_ROWS, SKELETON_PRESETS, SOLID_KEYS, STATS_BAR_DEFAULTS, STROKE_KEYS, SearchHeaderComponent, SearchSelectorComponent, SearchbarComponent, SecurityViewComponent, SegmentControlComponent, SelectSearchComponent, SessionListModalComponent, SessionService, SettingsHubComponent, ShareButtonsComponent, ShareProfileModalComponent, SimpleComponent, SkeletonComponent, SkeletonLayoutComponent, SkeletonService, SplashScreenService, StatsBarComponent, StatsCardComponent, StepperComponent, StorageService, SwipeCarouselComponent, SwitchOrgModalComponent, TRI_KEYS, TabbedContentComponent, TableSkeletonComponent, TabsComponent, Terminal404Component, TestimonialCardComponent, TestimonialCarouselComponent, TextComponent, TextInputComponent, TextareaInputComponent, ThemeOption, ThemeService, TicketCardComponent, TimelineComponent, TitleBlockComponent, TitleComponent, ToastService, ToggleInputComponent, TokenService, ToolbarActionType, ToolbarComponent, TransferOwnershipModalComponent, TranslatePipe, TypedCollection, UPDATE_BANNER_DEFAULT_CONTENT, UPDATE_BANNER_I18N_NAMESPACE, UpdateBannerComponent, UserAvatarComponent, UsernameInputComponent, VALTECH_ADS_CONFIG, VALTECH_APP_CONFIG, VALTECH_APP_VERSION, VALTECH_AUTH_CONFIG, VALTECH_COMPANY_LINKS, VALTECH_CONTENT_CONFIG, VALTECH_DEBUG_CONSOLE, VALTECH_DEFAULT_CONTENT, VALTECH_DONATION_CONFIG, VALTECH_FEEDBACK_CONFIG, VALTECH_FIREBASE_CONFIG, VALTECH_FOOTER_I18N, VALTECH_FOOTER_LOGO, VALTECH_LANGUAGE_SELECTOR, VALTECH_LEGAL_CONFIG, VALTECH_NETWORK_ERROR_KEY, VALTECH_SETTINGS_MENU_LINKS, VALTECH_SITE_PATHS, VALTECH_SOCIAL_LINKS, VALTECH_SPLASH_SCREEN, VALTECH_WEB_BASE_URLS, VERSION, ValtechErrorService, WizardComponent, WizardFooterComponent, applyDefaultValueToControl, authGuard, authInterceptor, authPasswordValidator, beautifyLegalArticle, blogPost, buildCompanyFooterProps, buildFooterLinks, buildLegalLinkResolver, buildPath, buildSettingsCards, button, collections, connectPageRefresh, createErrorStateProps, createFirebaseConfig, createGlowCardProps, createInitialPaginationState, createNumberFromToField, createPageState, createPermissionLabeler, createRefreshableStream, createTitleProps, docs, errorLoggingInterceptor, extractPathParams, generatePatternTiles, generateRandomTile, getAppInfo, getAppVersion, getCollectionPath, getDocumentId, getTimeOfDayKey, goToTop, guestGuard, hasEmulators, iconButton, interpretError, isAtEnd, isCollectionPath, isDocumentPath, isEmulatorMode, isIonicColor, isValidPath, joinPath, maxLength, mulberry32, news, parseMarkdownArticle, permissionGuard, permissionGuardFromRoute, provideLegalContent, provideSplashScreen, provideValtechAboutRoutes, provideValtechAccountRoutes, provideValtechAds, provideValtechApiKeysRoutes, provideValtechAppConfig, provideValtechAppVersion, provideValtechAppVersionHttp, provideValtechAuth, provideValtechAuthInterceptor, provideValtechContent, provideValtechDebugConsole, provideValtechDonations, provideValtechErrorHandling, provideValtechFeedback, provideValtechFirebase, provideValtechI18n, provideValtechLegal, provideValtechNotificationPreferencesRoutes, provideValtechNotificationsRoutes, provideValtechOrganizationRoutes, provideValtechPermissionsRoutes, provideValtechPreferencesRoutes, provideValtechPresets, provideValtechProfileRoutes, provideValtechSecurityRoutes, provideValtechSettingsRoutes, provideValtechSite, provideValtechSkeleton, query, renderPatternSvgInner, replaceSpecialChars, resolveColor, resolveInputDefaultValue, resolveIonicColor, resolveWebBaseUrl, roleGuard, storagePaths, superAdminGuard, toArticle };
|
|
67376
|
+
export { ACTION_CARD_DEFAULTS, AD_SIZE_MAP, API_TABLE_COLUMN_LABELS, APP_VERSION_PLATFORM_PLUGIN, APP_VERSION_REMOTE_PLUGIN, ARTICLE_CARD_DEFAULTS, ARTICLE_SPACING, ARTICLE_STRIP_DEFAULTS, AUTH_CTA_DEFAULTS, AVATAR_UPLOAD_DEFAULTS, AboutViewComponent, AccordionComponent, AccountViewComponent, ActionCardComponent, ActionHeaderComponent, ActionType, AdSlotComponent, AdsLoaderService, AdsService, AlertBoxComponent, AnalyticsErrorHandler, AnalyticsRouterTracker, AnalyticsService, AnimatedTerminalComponent, ApiKeyCreateModalComponent, ApiKeyService, ApiKeysModalComponent, ApiKeysViewComponent, AppConfigService, AppVersionService, ArticleBuilder, ArticleCardComponent, ArticleComponent, ArticleStripComponent, AttachmentUploaderComponent, AuthBackgroundComponent, AuthCtaComponent, AuthService, AuthStateService, AuthStorageService, AuthSyncService, AvatarComponent, AvatarUploadComponent, BOTTOM_NAV_DEFAULTS, BannerComponent, BlogPostBuilder, BottomNavComponent, BoxComponent, BreadcrumbComponent, ButtonComponent, ButtonGroupComponent, CALLOUT_LABELS, CHEV_KEYS, CIRCLE_KEYS, COMMON_COUNTRY_CODES, COMMON_CURRENCIES, CORNER_KEYS, CTA_CARD_DEFAULTS, CURRENCY_INFO, CardComponent, CardSection, CardType, CardsCarouselComponent, ChangeEmailModalComponent, ChangePasswordModalComponent, CheckInputComponent, CheckboxRadioInputComponent, ChipGroupComponent, CodeDisplayComponent, CommandDisplayComponent, CommentComponent, CommentInputComponent, CommentSectionComponent, CompanyFooterComponent, ComponentStates, ConfirmationDialogService, ContainerComponent, ContentLoaderComponent, ContentReactionComponent, ContentService, ContentTransformer, CookieBannerComponent, CountdownComponent, CreateOrgModalComponent, CtaCardComponent, CurrencyInputComponent, DEFAULT_ADS_CONFIG, DEFAULT_APP_CONFIG_SERVICE_CONFIG, DEFAULT_APP_VERSION_SERVICE_CONFIG, DEFAULT_AUTH_CONFIG, DEFAULT_BACK_HEADER, DEFAULT_BUTTON_PRESETS, DEFAULT_CANCEL_BUTTON, DEFAULT_CHECK_INTERVAL_MS, DEFAULT_CONFIRM_BUTTON, DEFAULT_COUNTDOWN_LABELS, DEFAULT_COUNTDOWN_LABELS_EN, DEFAULT_DEBUG_CONSOLE_CONFIG, DEFAULT_DONATION_CONFIG, DEFAULT_EMPTY_STATE, DEFAULT_EMULATOR_CONFIG, DEFAULT_FEEDBACK_CONFIG, DEFAULT_FEEDBACK_TYPE_OPTIONS, DEFAULT_HOME_HEADER, DEFAULT_INFINITE_LIST_METADATA, DEFAULT_LOGIN_LOGO, DEFAULT_MODAL_CANCEL_BUTTON, DEFAULT_MODAL_CONFIRM_BUTTON, DEFAULT_PAGE_SIZE_OPTIONS, DEFAULT_PLATFORMS, DEFAULT_PRESETS, DEFAULT_REFRESHER_METADATA, DEFAULT_SKELETON_CONFIG, DEFAULT_SPLASH_SCREEN_CONFIG, DataTableComponent, DateInputComponent, DatePickerComponent, DateRangeInputComponent, DebugConsoleComponent, DeleteAccountModalComponent, DetailSkeletonComponent, DeviceService, DisplayComponent, DividerComponent, DocsApiTableComponent, DocsBreadcrumbComponent, DocsBuilder, DocsCalloutComponent, DocsCodeExampleComponent, DocsLayoutComponent, DocsNavLinksComponent, DocsNavigationService, DocsPageComponent, DocsSearchComponent, DocsSectionComponent, DocsShellComponent, DocsSidebarComponent, DocsTocComponent, DonationService, DownloadService, EditOrgModalComponent, EmptyStateComponent, ExpandableTextComponent, FEATURES_LIST_DEFAULTS, FUN_MODAL_DEFAULTS, FabComponent, FaqComponent, FeaturesListComponent, FeedbackFormComponent, FeedbackService, FieldListComponent, FileInputComponent, FirebaseService, FirestoreCollectionFactory, FirestoreService, FontSizeOption, FontSizeService, FooterComponent, FooterLinksComponent, FormComponent, FormSkeletonComponent, FunHeaderComponent, FunModalComponent, GlassComponent, GlowCardComponent, GlowComponent, GridSkeletonComponent, HANDOFF_ROUTE_PARAM, HANDOFF_TOKEN_PARAM, HandoffService, HasPermissionDirective, HeaderComponent, HintComponent, HorizontalScrollComponent, HrefComponent, I18nService, IMAGE_DEFAULTS, INITIAL_AUTH_STATE, INITIAL_MFA_STATE, INVITATION_CARD_DEFAULTS, IONIC_COLORS$5 as IONIC_COLORS, Icon, IconComponent, IconService, ImageComponent, ImageCropComponent, ImageService, InAppBrowserService, InfiniteListComponent, InfoComponent, InputI18nHelper, InputType, InvitationCardComponent, InviteMemberModalComponent, ItemListComponent, LANG_STORAGE_KEY$1 as LANG_STORAGE_KEY, LEGAL_CONTENT_CONFIG, LOGIN_DEFAULTS, LandingSplitComponent, LandingStepsComponent, LanguageSelectorComponent, LayeredCardComponent, LegalContentService, LegalLinkService, LinkComponent, LinkProcessorService, LinkedProvidersComponent, LinksAccordionComponent, LinksCakeComponent, ListSkeletonComponent, LoadingDirective, LocalStorageService, LocaleService, LoginAttemptModalComponent, LoginComponent, MEDIA_OBJECT_DEFAULTS, MEMBER_CARD_DEFAULTS, METRIC_CARD_DEFAULTS, MODAL_SIZES, MOTIF_KEYS, MOTION, MaintenancePageComponent, MarkdownArticleParserService, MediaObjectComponent, MemberCardComponent, MemberDetailModalComponent, MemberImportModalComponent, MenuComponent, MessagingService, MetaService, MetricCardComponent, MfaModalComponent, ModalService, ModalShellComponent, MultiSelectSearchComponent, NUM_KEYS, NavigationService, NetworkBannerComponent, NetworkStatusService, NewsBuilder, NoContentComponent, NotesBoxComponent, NotificationActionService, NotificationPreferencesViewComponent, NotificationsService, NotificationsViewComponent, NumberFromToComponent, NumberStepperComponent, OAUTH_PROVIDERS_INFO, OAuthCallbackComponent, OAuthService, OrgInfoSheetComponent, OrgService, OrgSwitchService, OrganizationViewComponent, PATTERN_MOTIFS, PATTERN_PALETTES, PATTERN_STYLE_CONFIGS, PLATFORM_CONFIGS, PageContentComponent, PageLinksComponent, PageRefreshService, PageTemplateComponent, PageWavesComponent, PageWrapperComponent, PaginationComponent, PaginationService, PasswordInputComponent, PatternComponent, PermissionCatalogService, PermissionSelectorComponent, PermissionsModalComponent, PermissionsViewComponent, PhoneInputComponent, PillComponent, PinInputComponent, PlainCodeBoxComponent, PopoverSelectorComponent, PreferencesService, PreferencesViewComponent, PresetService, PriceTagComponent, ProcessLinksPipe, ProfileSkeletonComponent, ProfileViewComponent, ProgressBarComponent, ProgressRingComponent, ProgressStatusComponent, PrompterComponent, QR_PRESETS, QrCodeComponent, QrGeneratorService, QrScannerComponent, QueryBuilder, QuoteBoxComponent, REQUEST_STATUSES, RadioInputComponent, RangeInputComponent, RatingComponent, RefresherComponent, RequestFirestoreService, RequestFormBuilderService, RequestService, RightsFooterComponent, RotatingTextComponent, SEARCH_HEADER_DEFAULTS, SETTINGS_SECTIONS_CATALOG, SHAPE_KEYS, SHARE_PROFILE_MODAL_DEFAULTS, SKELETON_LAYOUT_DEFAULT_ROWS, SKELETON_PRESETS, SOLID_KEYS, STATS_BAR_DEFAULTS, STROKE_KEYS, SearchHeaderComponent, SearchSelectorComponent, SearchbarComponent, SecurityViewComponent, SegmentControlComponent, SelectSearchComponent, SessionListModalComponent, SessionService, SettingsHubComponent, ShareButtonsComponent, ShareProfileModalComponent, SimpleComponent, SkeletonComponent, SkeletonLayoutComponent, SkeletonService, SplashScreenService, StatsBarComponent, StatsCardComponent, StepperComponent, StorageService, SwipeCarouselComponent, SwitchOrgModalComponent, TRI_KEYS, TabbedContentComponent, TableSkeletonComponent, TabsComponent, Terminal404Component, TestimonialCardComponent, TestimonialCarouselComponent, TextComponent, TextInputComponent, TextareaInputComponent, ThemeOption, ThemeService, TicketCardComponent, TimelineComponent, TitleBlockComponent, TitleComponent, ToastService, ToggleInputComponent, TokenService, ToolbarActionType, ToolbarComponent, TransferOwnershipModalComponent, TranslatePipe, TypedCollection, UPDATE_BANNER_DEFAULT_CONTENT, UPDATE_BANNER_I18N_NAMESPACE, UpdateBannerComponent, UserAvatarComponent, UsernameInputComponent, VALTECH_ADS_CONFIG, VALTECH_APP_CONFIG, VALTECH_APP_VERSION, VALTECH_AUTH_CONFIG, VALTECH_COMPANY_LINKS, VALTECH_CONTENT_CONFIG, VALTECH_DEBUG_CONSOLE, VALTECH_DEFAULT_CONTENT, VALTECH_DONATION_CONFIG, VALTECH_FEEDBACK_CONFIG, VALTECH_FIREBASE_CONFIG, VALTECH_FOOTER_I18N, VALTECH_FOOTER_LOGO, VALTECH_LANGUAGE_SELECTOR, VALTECH_LEGAL_CONFIG, VALTECH_NETWORK_ERROR_KEY, VALTECH_SETTINGS_MENU_LINKS, VALTECH_SITE_PATHS, VALTECH_SOCIAL_LINKS, VALTECH_SPLASH_SCREEN, VALTECH_WEB_BASE_URLS, VERSION, ValtechErrorService, WizardComponent, WizardFooterComponent, applyDefaultValueToControl, authGuard, authInterceptor, authPasswordValidator, beautifyLegalArticle, blogPost, buildCompanyFooterProps, buildFooterLinks, buildLegalLinkResolver, buildPath, buildSettingsCards, button, collections, connectPageRefresh, createErrorStateProps, createFirebaseConfig, createGlowCardProps, createInitialPaginationState, createNumberFromToField, createPageState, createPermissionLabeler, createRefreshableStream, createTitleProps, docs, errorLoggingInterceptor, extractPathParams, generatePatternTiles, generateRandomTile, getAppInfo, getAppVersion, getCollectionPath, getDocumentId, getTimeOfDayKey, goToTop, guestGuard, hasEmulators, iconButton, interpretError, isAtEnd, isCollectionPath, isDocumentPath, isEmulatorMode, isIonicColor, isValidPath, joinPath, maxLength, mulberry32, news, parseMarkdownArticle, permissionGuard, permissionGuardFromRoute, provideLegalContent, provideSplashScreen, provideValtechAboutRoutes, provideValtechAccountRoutes, provideValtechAds, provideValtechApiKeysRoutes, provideValtechAppConfig, provideValtechAppVersion, provideValtechAppVersionHttp, provideValtechAuth, provideValtechAuthInterceptor, provideValtechContent, provideValtechDebugConsole, provideValtechDonations, provideValtechErrorHandling, provideValtechFeedback, provideValtechFirebase, provideValtechI18n, provideValtechLegal, provideValtechNotificationPreferencesRoutes, provideValtechNotificationsRoutes, provideValtechOrganizationRoutes, provideValtechPermissionsRoutes, provideValtechPreferencesRoutes, provideValtechPresets, provideValtechProfileRoutes, provideValtechSecurityRoutes, provideValtechSettingsRoutes, provideValtechSite, provideValtechSkeleton, query, renderPatternSvgInner, replaceSpecialChars, resolveColor, resolveInputDefaultValue, resolveIonicColor, resolveWebBaseUrl, roleGuard, storagePaths, superAdminGuard, toArticle };
|
|
67208
67377
|
//# sourceMappingURL=valtech-components.mjs.map
|