valtech-components 2.0.558 → 2.0.559

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.
@@ -3319,6 +3319,96 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
3319
3319
  args: [{ providedIn: 'root' }]
3320
3320
  }], ctorParameters: () => [] });
3321
3321
 
3322
+ /**
3323
+ * Helper para resolver traducciones i18n en InputMetadata y FormMetadata.
3324
+ *
3325
+ * @example
3326
+ * ```typescript
3327
+ * helper = inject(InputI18nHelper);
3328
+ *
3329
+ * // Resolver un input
3330
+ * emailInput = this.helper.resolveInput({
3331
+ * ...baseEmailInput,
3332
+ * labelKey: 'email',
3333
+ * placeholderKey: 'emailPlaceholder',
3334
+ * errorKeys: { required: 'emailRequired', email: 'emailInvalid' },
3335
+ * i18nNamespace: '_auth',
3336
+ * });
3337
+ *
3338
+ * // Resolver un form completo
3339
+ * loginForm = this.helper.resolveForm(loginFormConfig);
3340
+ * ```
3341
+ */
3342
+ class InputI18nHelper {
3343
+ constructor() {
3344
+ this.i18n = inject(I18nService);
3345
+ }
3346
+ /**
3347
+ * Resuelve traducciones para un InputMetadata.
3348
+ * Si hay keys de i18n, las resuelve; sino, usa los valores directos.
3349
+ */
3350
+ resolveInput(input) {
3351
+ const namespace = input.i18nNamespace || '_global';
3352
+ return {
3353
+ ...input,
3354
+ label: input.labelKey
3355
+ ? this.i18n.t(input.labelKey, namespace)
3356
+ : input.label || '',
3357
+ placeholder: input.placeholderKey
3358
+ ? this.i18n.t(input.placeholderKey, namespace)
3359
+ : input.placeholder || '',
3360
+ hint: input.hintKey ? this.i18n.t(input.hintKey, namespace) : input.hint || '',
3361
+ errors: input.errorKeys
3362
+ ? this.resolveErrors(input.errorKeys, namespace)
3363
+ : input.errors || {},
3364
+ };
3365
+ }
3366
+ /**
3367
+ * Resuelve traducciones para un FormMetadata completo.
3368
+ */
3369
+ resolveForm(form) {
3370
+ const namespace = form.i18nNamespace || '_global';
3371
+ return {
3372
+ ...form,
3373
+ name: form.nameKey ? this.i18n.t(form.nameKey, namespace) : form.name || '',
3374
+ sections: form.sections.map((section) => ({
3375
+ ...section,
3376
+ name: section.nameKey
3377
+ ? this.i18n.t(section.nameKey, namespace)
3378
+ : section.name || '',
3379
+ fields: section.fields.map((field) => this.resolveInput({ ...field, i18nNamespace: namespace })),
3380
+ })),
3381
+ actions: this.resolveButton(form.actions, namespace),
3382
+ };
3383
+ }
3384
+ /**
3385
+ * Resuelve traducciones para un ButtonMetadata.
3386
+ */
3387
+ resolveButton(button, namespace) {
3388
+ const ns = namespace || button.i18nNamespace || '_global';
3389
+ return {
3390
+ ...button,
3391
+ text: button.textKey ? this.i18n.t(button.textKey, ns) : button.text,
3392
+ };
3393
+ }
3394
+ /**
3395
+ * Resuelve un mapa de error keys a mensajes traducidos.
3396
+ */
3397
+ resolveErrors(errorKeys, namespace) {
3398
+ const resolved = {};
3399
+ for (const [validatorKey, i18nKey] of Object.entries(errorKeys)) {
3400
+ resolved[validatorKey] = this.i18n.t(i18nKey, namespace);
3401
+ }
3402
+ return resolved;
3403
+ }
3404
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: InputI18nHelper, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
3405
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: InputI18nHelper, providedIn: 'root' }); }
3406
+ }
3407
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: InputI18nHelper, decorators: [{
3408
+ type: Injectable,
3409
+ args: [{ providedIn: 'root' }]
3410
+ }] });
3411
+
3322
3412
  /**
3323
3413
  * Pipe de traducción para templates.
3324
3414
  *
@@ -3677,6 +3767,170 @@ const VALTECH_DEFAULT_CONTENT = {
3677
3767
  paymentFailed: 'Failed',
3678
3768
  },
3679
3769
  },
3770
+ _auth: {
3771
+ es: {
3772
+ // Login
3773
+ loginTitle: 'Iniciar sesión',
3774
+ email: 'Correo electrónico',
3775
+ emailPlaceholder: 'tu@email.com',
3776
+ emailRequired: 'El correo es requerido',
3777
+ emailInvalid: 'Ingresa un correo válido',
3778
+ password: 'Contraseña',
3779
+ passwordPlaceholder: '••••••••',
3780
+ passwordRequired: 'La contraseña es requerida',
3781
+ loginSubmit: 'Iniciar sesión',
3782
+ // OAuth
3783
+ orContinueWith: 'o continúa con',
3784
+ continueWithGoogle: 'Continuar con Google',
3785
+ continueWithApple: 'Continuar con Apple',
3786
+ continueWithMicrosoft: 'Continuar con Microsoft',
3787
+ connecting: 'Conectando...',
3788
+ // Register
3789
+ registerTitle: 'Crear cuenta',
3790
+ fullName: 'Nombre completo',
3791
+ namePlaceholder: 'Tu nombre',
3792
+ nameRequired: 'El nombre es requerido',
3793
+ nameMinLength: 'Mínimo 2 caracteres',
3794
+ passwordMinLength: 'Mínimo 8 caracteres',
3795
+ registerSubmit: 'Registrarse',
3796
+ noAccount: '¿No tienes cuenta?',
3797
+ register: 'Registrarse',
3798
+ hasAccount: '¿Ya tienes cuenta?',
3799
+ signIn: 'Iniciar sesión',
3800
+ // Verify email
3801
+ verifyTitle: 'Verificar correo',
3802
+ verifyDescription: 'Ingresa el código de verificación enviado a tu correo.',
3803
+ verifySubmit: 'Verificar',
3804
+ codeRequired: 'El código es requerido',
3805
+ codeMinLength: 'Ingresa los 6 dígitos',
3806
+ noCodeReceived: '¿No has recibido tu código?',
3807
+ resend: 'Reenviar',
3808
+ resendIn: 'Reenviar en {seconds}s',
3809
+ codeSent: 'Código reenviado. Revisa tu email.',
3810
+ emailVerified: '¡Email verificado! Bienvenido.',
3811
+ // MFA
3812
+ mfaTitle: 'Verificación MFA',
3813
+ mfaTOTP: 'Ingresa el código de tu app de autenticación',
3814
+ mfaEmail: 'Ingresa el código enviado a tu correo',
3815
+ mfaSMS: 'Ingresa el código enviado a tu teléfono',
3816
+ // Forgot/Reset password
3817
+ forgotTitle: 'Recuperar contraseña',
3818
+ forgotDescription: 'Ingresa tu correo electrónico y te enviaremos un código.',
3819
+ forgotSubmit: 'Enviar código',
3820
+ forgotLink: '¿Olvidaste tu contraseña?',
3821
+ recoverPassword: 'Recuperar contraseña',
3822
+ resetTitle: 'Restablecer contraseña',
3823
+ resetDescription: 'Hemos enviado un código de verificación a tu correo.',
3824
+ newPassword: 'Nueva contraseña',
3825
+ newPasswordHint: 'Mínimo 8 caracteres',
3826
+ resetSubmit: 'Cambiar contraseña',
3827
+ passwordUpdated: '¡Contraseña actualizada! Ya puedes iniciar sesión.',
3828
+ // Legal
3829
+ legalPrefix: 'Utilizamos los servicios de',
3830
+ legalSuffix: 'para ofrecerte una experiencia segura. Al iniciar sesión, aceptas nuestros',
3831
+ termsAndConditions: 'Términos y Condiciones',
3832
+ and: 'y',
3833
+ privacyPolicy: 'Política de Privacidad',
3834
+ // Toasts
3835
+ welcome: '¡Bienvenido!',
3836
+ completeAllFields: 'Completa todos los campos.',
3837
+ enterEmail: 'Ingresa tu correo electrónico.',
3838
+ // Errors (mapeados de códigos backend)
3839
+ errorInvalidCredentials: 'Correo o contraseña incorrectos',
3840
+ errorEmailNotVerified: 'Debes verificar tu correo electrónico',
3841
+ errorAccountSuspended: 'Tu cuenta ha sido suspendida',
3842
+ errorEmailExists: 'Este correo ya está registrado',
3843
+ errorWeakPassword: 'La contraseña es muy débil',
3844
+ errorInvalidCode: 'Código incorrecto',
3845
+ errorExpiredCode: 'El código ha expirado. Solicita uno nuevo.',
3846
+ errorTooManyAttempts: 'Demasiados intentos, intenta más tarde',
3847
+ errorMFAInvalidCode: 'Código de verificación incorrecto',
3848
+ errorPopupBlocked: 'Por favor, permite ventanas emergentes',
3849
+ errorOAuthFailed: 'Error de autenticación',
3850
+ errorGeneric: 'Ha ocurrido un error. Intenta de nuevo.',
3851
+ },
3852
+ en: {
3853
+ // Login
3854
+ loginTitle: 'Sign in',
3855
+ email: 'Email',
3856
+ emailPlaceholder: 'you@email.com',
3857
+ emailRequired: 'Email is required',
3858
+ emailInvalid: 'Enter a valid email',
3859
+ password: 'Password',
3860
+ passwordPlaceholder: '••••••••',
3861
+ passwordRequired: 'Password is required',
3862
+ loginSubmit: 'Sign in',
3863
+ // OAuth
3864
+ orContinueWith: 'or continue with',
3865
+ continueWithGoogle: 'Continue with Google',
3866
+ continueWithApple: 'Continue with Apple',
3867
+ continueWithMicrosoft: 'Continue with Microsoft',
3868
+ connecting: 'Connecting...',
3869
+ // Register
3870
+ registerTitle: 'Create account',
3871
+ fullName: 'Full name',
3872
+ namePlaceholder: 'Your name',
3873
+ nameRequired: 'Name is required',
3874
+ nameMinLength: 'Minimum 2 characters',
3875
+ passwordMinLength: 'Minimum 8 characters',
3876
+ registerSubmit: 'Sign up',
3877
+ noAccount: "Don't have an account?",
3878
+ register: 'Sign up',
3879
+ hasAccount: 'Already have an account?',
3880
+ signIn: 'Sign in',
3881
+ // Verify email
3882
+ verifyTitle: 'Verify email',
3883
+ verifyDescription: 'Enter the verification code sent to your email.',
3884
+ verifySubmit: 'Verify',
3885
+ codeRequired: 'Code is required',
3886
+ codeMinLength: 'Enter all 6 digits',
3887
+ noCodeReceived: "Didn't receive your code?",
3888
+ resend: 'Resend',
3889
+ resendIn: 'Resend in {seconds}s',
3890
+ codeSent: 'Code resent. Check your email.',
3891
+ emailVerified: 'Email verified! Welcome.',
3892
+ // MFA
3893
+ mfaTitle: 'MFA Verification',
3894
+ mfaTOTP: 'Enter the code from your authenticator app',
3895
+ mfaEmail: 'Enter the code sent to your email',
3896
+ mfaSMS: 'Enter the code sent to your phone',
3897
+ // Forgot/Reset password
3898
+ forgotTitle: 'Recover password',
3899
+ forgotDescription: 'Enter your email and we will send you a code.',
3900
+ forgotSubmit: 'Send code',
3901
+ forgotLink: 'Forgot your password?',
3902
+ recoverPassword: 'Recover password',
3903
+ resetTitle: 'Reset password',
3904
+ resetDescription: 'We have sent a verification code to your email.',
3905
+ newPassword: 'New password',
3906
+ newPasswordHint: 'Minimum 8 characters',
3907
+ resetSubmit: 'Change password',
3908
+ passwordUpdated: 'Password updated! You can now sign in.',
3909
+ // Legal
3910
+ legalPrefix: 'We use the services of',
3911
+ legalSuffix: 'to offer you a secure experience. By signing in, you accept our',
3912
+ termsAndConditions: 'Terms and Conditions',
3913
+ and: 'and',
3914
+ privacyPolicy: 'Privacy Policy',
3915
+ // Toasts
3916
+ welcome: 'Welcome!',
3917
+ completeAllFields: 'Complete all fields.',
3918
+ enterEmail: 'Enter your email.',
3919
+ // Errors
3920
+ errorInvalidCredentials: 'Incorrect email or password',
3921
+ errorEmailNotVerified: 'You must verify your email',
3922
+ errorAccountSuspended: 'Your account has been suspended',
3923
+ errorEmailExists: 'This email is already registered',
3924
+ errorWeakPassword: 'Password is too weak',
3925
+ errorInvalidCode: 'Incorrect code',
3926
+ errorExpiredCode: 'Code has expired. Request a new one.',
3927
+ errorTooManyAttempts: 'Too many attempts, try again later',
3928
+ errorMFAInvalidCode: 'Incorrect verification code',
3929
+ errorPopupBlocked: 'Please allow pop-ups',
3930
+ errorOAuthFailed: 'Authentication error',
3931
+ errorGeneric: 'An error occurred. Please try again.',
3932
+ },
3933
+ },
3680
3934
  };
3681
3935
 
3682
3936
  /**
@@ -28923,6 +29177,8 @@ class LoginComponent {
28923
29177
  this.authService = inject(AuthService);
28924
29178
  this.toastService = inject(ToastService);
28925
29179
  this.router = inject(Router);
29180
+ this.i18n = inject(I18nService);
29181
+ this.i18nHelper = inject(InputI18nHelper);
28926
29182
  // Timers
28927
29183
  this.resendTimer = null;
28928
29184
  this.resetResendTimer = null;
@@ -28940,283 +29196,351 @@ class LoginComponent {
28940
29196
  // ==========================================
28941
29197
  // LOGIN FORM
28942
29198
  // ==========================================
28943
- this.emailInput = {
28944
- type: InputType.EMAIL,
28945
- label: 'Correo electrónico',
28946
- name: 'email',
28947
- token: 'login-email',
28948
- hint: '',
28949
- placeholder: 'tu@email.com',
28950
- errors: {
28951
- required: 'El correo es requerido',
28952
- email: 'Ingresa un correo válido',
28953
- },
28954
- validators: [Validators.required, Validators.email],
28955
- order: 0,
28956
- state: ComponentStates.ENABLED,
28957
- control: undefined,
28958
- };
28959
- this.passwordInput = {
28960
- type: InputType.PASSWORD,
28961
- label: 'Contraseña',
28962
- name: 'password',
28963
- token: 'login-password',
28964
- hint: '',
28965
- placeholder: '••••••••',
28966
- errors: {
28967
- required: 'La contraseña es requerida',
28968
- },
28969
- validators: [Validators.required],
28970
- order: 1,
28971
- state: ComponentStates.ENABLED,
28972
- control: undefined,
28973
- };
28974
- this.loginFormProps = {
28975
- name: 'Iniciar sesión',
29199
+ this._loginFormState = ComponentStates.ENABLED;
29200
+ // ==========================================
29201
+ // REGISTER FORM
29202
+ // ==========================================
29203
+ this._registerFormState = ComponentStates.ENABLED;
29204
+ // ==========================================
29205
+ // VERIFY EMAIL FORM
29206
+ // ==========================================
29207
+ this._verifyFormState = ComponentStates.ENABLED;
29208
+ this._verifyFormSectionName = '';
29209
+ // ==========================================
29210
+ // MFA VERIFY FORM
29211
+ // ==========================================
29212
+ this._mfaVerifyFormState = ComponentStates.ENABLED;
29213
+ this._mfaMethod = 'TOTP';
29214
+ // ==========================================
29215
+ // FORGOT PASSWORD FORM
29216
+ // ==========================================
29217
+ this._forgotPasswordFormState = ComponentStates.ENABLED;
29218
+ // ==========================================
29219
+ // RESET PASSWORD FORM
29220
+ // ==========================================
29221
+ this._resetPasswordFormState = ComponentStates.ENABLED;
29222
+ this._resetFormSectionName = '';
29223
+ }
29224
+ /**
29225
+ * Helper method for translating auth-related strings.
29226
+ * Exposed to template for dynamic text.
29227
+ */
29228
+ t(key, data) {
29229
+ return this.i18n.t(key, '_auth', data);
29230
+ }
29231
+ // Resolved props with defaults
29232
+ get config() {
29233
+ return { ...LOGIN_DEFAULTS, ...this.props };
29234
+ }
29235
+ get loginFormProps() {
29236
+ return this.i18nHelper.resolveForm({
29237
+ nameKey: 'loginTitle',
29238
+ i18nNamespace: '_auth',
28976
29239
  sections: [
28977
29240
  {
28978
29241
  name: '',
28979
29242
  order: 0,
28980
- fields: [this.emailInput, this.passwordInput],
29243
+ fields: [
29244
+ {
29245
+ type: InputType.EMAIL,
29246
+ name: 'email',
29247
+ token: 'login-email',
29248
+ hint: '',
29249
+ labelKey: 'email',
29250
+ placeholderKey: 'emailPlaceholder',
29251
+ errorKeys: {
29252
+ required: 'emailRequired',
29253
+ email: 'emailInvalid',
29254
+ },
29255
+ validators: [Validators.required, Validators.email],
29256
+ order: 0,
29257
+ state: ComponentStates.ENABLED,
29258
+ },
29259
+ {
29260
+ type: InputType.PASSWORD,
29261
+ name: 'password',
29262
+ token: 'login-password',
29263
+ hint: '',
29264
+ labelKey: 'password',
29265
+ placeholderKey: 'passwordPlaceholder',
29266
+ errorKeys: {
29267
+ required: 'passwordRequired',
29268
+ },
29269
+ validators: [Validators.required],
29270
+ order: 1,
29271
+ state: ComponentStates.ENABLED,
29272
+ },
29273
+ ],
28981
29274
  },
28982
29275
  ],
28983
29276
  actions: {
28984
- ...SolidDefaultBlock('Iniciar sesión', 'submit'),
29277
+ ...SolidDefaultBlock('', 'submit'),
28985
29278
  token: 'login-submit',
29279
+ textKey: 'loginSubmit',
28986
29280
  },
28987
- state: ComponentStates.ENABLED,
28988
- };
28989
- // ==========================================
28990
- // REGISTER FORM
28991
- // ==========================================
28992
- this.nameInput = {
28993
- type: InputType.TEXT,
28994
- label: 'Nombre completo',
28995
- name: 'name',
28996
- token: 'register-name',
28997
- hint: '',
28998
- placeholder: 'Tu nombre',
28999
- errors: {
29000
- required: 'El nombre es requerido',
29001
- minlength: 'Mínimo 2 caracteres',
29002
- },
29003
- validators: [Validators.required, Validators.minLength(2)],
29004
- order: 0,
29005
- state: ComponentStates.ENABLED,
29006
- control: undefined,
29007
- };
29008
- this.registerEmailInput = {
29009
- type: InputType.EMAIL,
29010
- label: 'Correo electrónico',
29011
- name: 'email',
29012
- token: 'register-email',
29013
- hint: '',
29014
- placeholder: 'tu@email.com',
29015
- errors: {
29016
- required: 'El correo es requerido',
29017
- email: 'Ingresa un correo válido',
29018
- },
29019
- validators: [Validators.required, Validators.email],
29020
- order: 1,
29021
- state: ComponentStates.ENABLED,
29022
- control: undefined,
29023
- };
29024
- this.registerPasswordInput = {
29025
- type: InputType.PASSWORD,
29026
- label: 'Contraseña',
29027
- name: 'password',
29028
- token: 'register-password',
29029
- hint: '',
29030
- placeholder: '••••••••',
29031
- errors: {
29032
- required: 'La contraseña es requerida',
29033
- minlength: 'Mínimo 8 caracteres',
29034
- },
29035
- validators: [Validators.required, Validators.minLength(8)],
29036
- order: 2,
29037
- state: ComponentStates.ENABLED,
29038
- control: undefined,
29039
- };
29040
- this.registerFormProps = {
29041
- name: 'Crear cuenta',
29281
+ state: this._loginFormState,
29282
+ });
29283
+ }
29284
+ set loginFormState(value) {
29285
+ this._loginFormState = value;
29286
+ }
29287
+ get registerFormProps() {
29288
+ return this.i18nHelper.resolveForm({
29289
+ nameKey: 'registerTitle',
29290
+ i18nNamespace: '_auth',
29042
29291
  sections: [
29043
29292
  {
29044
29293
  name: '',
29045
29294
  order: 0,
29046
- fields: [this.nameInput, this.registerEmailInput, this.registerPasswordInput],
29295
+ fields: [
29296
+ {
29297
+ type: InputType.TEXT,
29298
+ name: 'name',
29299
+ token: 'register-name',
29300
+ hint: '',
29301
+ labelKey: 'fullName',
29302
+ placeholderKey: 'namePlaceholder',
29303
+ errorKeys: {
29304
+ required: 'nameRequired',
29305
+ minlength: 'nameMinLength',
29306
+ },
29307
+ validators: [Validators.required, Validators.minLength(2)],
29308
+ order: 0,
29309
+ state: ComponentStates.ENABLED,
29310
+ },
29311
+ {
29312
+ type: InputType.EMAIL,
29313
+ name: 'email',
29314
+ token: 'register-email',
29315
+ hint: '',
29316
+ labelKey: 'email',
29317
+ placeholderKey: 'emailPlaceholder',
29318
+ errorKeys: {
29319
+ required: 'emailRequired',
29320
+ email: 'emailInvalid',
29321
+ },
29322
+ validators: [Validators.required, Validators.email],
29323
+ order: 1,
29324
+ state: ComponentStates.ENABLED,
29325
+ },
29326
+ {
29327
+ type: InputType.PASSWORD,
29328
+ name: 'password',
29329
+ token: 'register-password',
29330
+ hint: '',
29331
+ labelKey: 'password',
29332
+ placeholderKey: 'passwordPlaceholder',
29333
+ errorKeys: {
29334
+ required: 'passwordRequired',
29335
+ minlength: 'passwordMinLength',
29336
+ },
29337
+ validators: [Validators.required, Validators.minLength(8)],
29338
+ order: 2,
29339
+ state: ComponentStates.ENABLED,
29340
+ },
29341
+ ],
29047
29342
  },
29048
29343
  ],
29049
29344
  actions: {
29050
- ...SolidDefaultBlock('Registrarse', 'submit'),
29345
+ ...SolidDefaultBlock('', 'submit'),
29051
29346
  token: 'register-submit',
29347
+ textKey: 'registerSubmit',
29052
29348
  },
29053
- state: ComponentStates.ENABLED,
29054
- };
29055
- // ==========================================
29056
- // VERIFY EMAIL FORM
29057
- // ==========================================
29058
- this.verifyPinInput = {
29059
- type: InputType.PIN_CODE,
29060
- label: '',
29061
- name: 'code',
29062
- token: 'verify-pin',
29063
- hint: '',
29064
- placeholder: '',
29065
- errors: {
29066
- required: 'El código es requerido',
29067
- minlength: 'Ingresa los 6 dígitos',
29068
- },
29069
- validators: [Validators.required, Validators.minLength(6)],
29070
- order: 0,
29071
- state: ComponentStates.ENABLED,
29072
- control: undefined,
29073
- length: 6,
29074
- allowNumbersOnly: true,
29075
- autoFocus: true,
29076
- };
29077
- this.verifyFormProps = {
29078
- name: 'Verificar correo',
29349
+ state: this._registerFormState,
29350
+ });
29351
+ }
29352
+ set registerFormState(value) {
29353
+ this._registerFormState = value;
29354
+ }
29355
+ get verifyFormProps() {
29356
+ return this.i18nHelper.resolveForm({
29357
+ nameKey: 'verifyTitle',
29358
+ i18nNamespace: '_auth',
29079
29359
  sections: [
29080
29360
  {
29081
- name: 'Ingresa el código de verificación enviado a tu correo.',
29361
+ name: this._verifyFormSectionName || this.t('verifyDescription'),
29082
29362
  order: 0,
29083
- fields: [this.verifyPinInput],
29363
+ fields: [
29364
+ {
29365
+ type: InputType.PIN_CODE,
29366
+ label: '',
29367
+ name: 'code',
29368
+ token: 'verify-pin',
29369
+ hint: '',
29370
+ placeholder: '',
29371
+ errorKeys: {
29372
+ required: 'codeRequired',
29373
+ minlength: 'codeMinLength',
29374
+ },
29375
+ validators: [Validators.required, Validators.minLength(6)],
29376
+ order: 0,
29377
+ state: ComponentStates.ENABLED,
29378
+ length: 6,
29379
+ allowNumbersOnly: true,
29380
+ autoFocus: true,
29381
+ },
29382
+ ],
29084
29383
  },
29085
29384
  ],
29086
29385
  actions: {
29087
- ...SolidDefaultBlock('Verificar', 'submit'),
29386
+ ...SolidDefaultBlock('', 'submit'),
29088
29387
  token: 'verify-submit',
29388
+ textKey: 'verifySubmit',
29089
29389
  },
29090
- state: ComponentStates.ENABLED,
29091
- };
29092
- // ==========================================
29093
- // MFA VERIFY FORM
29094
- // ==========================================
29095
- this.mfaPinInput = {
29096
- type: InputType.PIN_CODE,
29097
- label: '',
29098
- name: 'code',
29099
- token: 'mfa-pin',
29100
- hint: '',
29101
- placeholder: '',
29102
- errors: {
29103
- required: 'El código es requerido',
29104
- minlength: 'Ingresa los 6 dígitos',
29105
- },
29106
- validators: [Validators.required, Validators.minLength(6)],
29107
- order: 0,
29108
- state: ComponentStates.ENABLED,
29109
- control: undefined,
29110
- length: 6,
29111
- allowNumbersOnly: true,
29112
- autoFocus: true,
29113
- };
29114
- this.mfaVerifyFormProps = {
29115
- name: 'Verificación MFA',
29390
+ state: this._verifyFormState,
29391
+ });
29392
+ }
29393
+ set verifyFormState(value) {
29394
+ this._verifyFormState = value;
29395
+ }
29396
+ set verifyFormSectionName(value) {
29397
+ this._verifyFormSectionName = value;
29398
+ }
29399
+ get mfaVerifyFormProps() {
29400
+ const sectionName = this._mfaMethod === 'TOTP'
29401
+ ? this.t('mfaTOTP')
29402
+ : this._mfaMethod === 'EMAIL'
29403
+ ? this.t('mfaEmail')
29404
+ : this.t('mfaSMS');
29405
+ return this.i18nHelper.resolveForm({
29406
+ nameKey: 'mfaTitle',
29407
+ i18nNamespace: '_auth',
29116
29408
  sections: [
29117
29409
  {
29118
- name: '',
29410
+ name: sectionName,
29119
29411
  order: 0,
29120
- fields: [this.mfaPinInput],
29412
+ fields: [
29413
+ {
29414
+ type: InputType.PIN_CODE,
29415
+ label: '',
29416
+ name: 'code',
29417
+ token: 'mfa-pin',
29418
+ hint: '',
29419
+ placeholder: '',
29420
+ errorKeys: {
29421
+ required: 'codeRequired',
29422
+ minlength: 'codeMinLength',
29423
+ },
29424
+ validators: [Validators.required, Validators.minLength(6)],
29425
+ order: 0,
29426
+ state: ComponentStates.ENABLED,
29427
+ length: 6,
29428
+ allowNumbersOnly: true,
29429
+ autoFocus: true,
29430
+ },
29431
+ ],
29121
29432
  },
29122
29433
  ],
29123
29434
  actions: {
29124
- ...SolidDefaultBlock('Verificar', 'submit'),
29435
+ ...SolidDefaultBlock('', 'submit'),
29125
29436
  token: 'mfa-verify-submit',
29437
+ textKey: 'verifySubmit',
29126
29438
  },
29127
- state: ComponentStates.ENABLED,
29128
- };
29129
- // ==========================================
29130
- // FORGOT PASSWORD FORM
29131
- // ==========================================
29132
- this.forgotEmailInput = {
29133
- type: InputType.EMAIL,
29134
- label: '',
29135
- name: 'email',
29136
- token: 'forgot-email',
29137
- hint: '',
29138
- placeholder: 'tu@email.com',
29139
- errors: {
29140
- required: 'El correo es requerido',
29141
- email: 'Ingresa un correo válido',
29142
- },
29143
- validators: [Validators.required, Validators.email],
29144
- order: 0,
29145
- state: ComponentStates.ENABLED,
29146
- control: undefined,
29147
- };
29148
- this.forgotPasswordFormProps = {
29149
- name: 'Recuperar contraseña',
29439
+ state: this._mfaVerifyFormState,
29440
+ });
29441
+ }
29442
+ set mfaVerifyFormState(value) {
29443
+ this._mfaVerifyFormState = value;
29444
+ }
29445
+ set mfaMethod(value) {
29446
+ this._mfaMethod = value;
29447
+ }
29448
+ get forgotPasswordFormProps() {
29449
+ return this.i18nHelper.resolveForm({
29450
+ nameKey: 'forgotTitle',
29451
+ i18nNamespace: '_auth',
29150
29452
  sections: [
29151
29453
  {
29152
- name: 'Ingresa tu correo electrónico y te enviaremos un código para restablecer tu contraseña.',
29454
+ name: this.t('forgotDescription'),
29153
29455
  order: 0,
29154
- fields: [this.forgotEmailInput],
29456
+ fields: [
29457
+ {
29458
+ type: InputType.EMAIL,
29459
+ label: '',
29460
+ name: 'email',
29461
+ token: 'forgot-email',
29462
+ hint: '',
29463
+ placeholderKey: 'emailPlaceholder',
29464
+ errorKeys: {
29465
+ required: 'emailRequired',
29466
+ email: 'emailInvalid',
29467
+ },
29468
+ validators: [Validators.required, Validators.email],
29469
+ order: 0,
29470
+ state: ComponentStates.ENABLED,
29471
+ },
29472
+ ],
29155
29473
  },
29156
29474
  ],
29157
29475
  actions: {
29158
- ...SolidDefaultBlock('Enviar código', 'submit'),
29476
+ ...SolidDefaultBlock('', 'submit'),
29159
29477
  token: 'forgot-submit',
29478
+ textKey: 'forgotSubmit',
29160
29479
  },
29161
- state: ComponentStates.ENABLED,
29162
- };
29163
- // ==========================================
29164
- // RESET PASSWORD FORM
29165
- // ==========================================
29166
- this.resetPinInput = {
29167
- type: InputType.PIN_CODE,
29168
- label: '',
29169
- name: 'code',
29170
- token: 'reset-pin',
29171
- hint: '',
29172
- placeholder: '',
29173
- errors: {
29174
- required: 'El código es requerido',
29175
- minlength: 'Ingresa los 6 dígitos',
29176
- },
29177
- validators: [Validators.required, Validators.minLength(6)],
29178
- order: 0,
29179
- state: ComponentStates.ENABLED,
29180
- control: undefined,
29181
- length: 6,
29182
- allowNumbersOnly: true,
29183
- autoFocus: true,
29184
- };
29185
- this.newPasswordInput = {
29186
- type: InputType.PASSWORD,
29187
- label: 'Nueva contraseña',
29188
- name: 'newPassword',
29189
- token: 'reset-new-password',
29190
- hint: 'Mínimo 8 caracteres',
29191
- placeholder: '••••••••',
29192
- errors: {
29193
- required: 'La contraseña es requerida',
29194
- minlength: 'Mínimo 8 caracteres',
29195
- },
29196
- validators: [Validators.required, Validators.minLength(8)],
29197
- order: 1,
29198
- state: ComponentStates.ENABLED,
29199
- control: undefined,
29200
- };
29201
- this.resetPasswordFormProps = {
29202
- name: 'Restablecer contraseña',
29480
+ state: this._forgotPasswordFormState,
29481
+ });
29482
+ }
29483
+ set forgotPasswordFormState(value) {
29484
+ this._forgotPasswordFormState = value;
29485
+ }
29486
+ get resetPasswordFormProps() {
29487
+ return this.i18nHelper.resolveForm({
29488
+ nameKey: 'resetTitle',
29489
+ i18nNamespace: '_auth',
29203
29490
  sections: [
29204
29491
  {
29205
- name: 'Hemos enviado un código de verificación a tu correo.',
29492
+ name: this._resetFormSectionName || this.t('resetDescription'),
29206
29493
  order: 0,
29207
- fields: [this.resetPinInput, this.newPasswordInput],
29494
+ fields: [
29495
+ {
29496
+ type: InputType.PIN_CODE,
29497
+ label: '',
29498
+ name: 'code',
29499
+ token: 'reset-pin',
29500
+ hint: '',
29501
+ placeholder: '',
29502
+ errorKeys: {
29503
+ required: 'codeRequired',
29504
+ minlength: 'codeMinLength',
29505
+ },
29506
+ validators: [Validators.required, Validators.minLength(6)],
29507
+ order: 0,
29508
+ state: ComponentStates.ENABLED,
29509
+ length: 6,
29510
+ allowNumbersOnly: true,
29511
+ autoFocus: true,
29512
+ },
29513
+ {
29514
+ type: InputType.PASSWORD,
29515
+ name: 'newPassword',
29516
+ token: 'reset-new-password',
29517
+ labelKey: 'newPassword',
29518
+ hintKey: 'newPasswordHint',
29519
+ placeholderKey: 'passwordPlaceholder',
29520
+ errorKeys: {
29521
+ required: 'passwordRequired',
29522
+ minlength: 'passwordMinLength',
29523
+ },
29524
+ validators: [Validators.required, Validators.minLength(8)],
29525
+ order: 1,
29526
+ state: ComponentStates.ENABLED,
29527
+ },
29528
+ ],
29208
29529
  },
29209
29530
  ],
29210
29531
  actions: {
29211
- ...SolidDefaultBlock('Cambiar contraseña', 'submit'),
29532
+ ...SolidDefaultBlock('', 'submit'),
29212
29533
  token: 'reset-submit',
29534
+ textKey: 'resetSubmit',
29213
29535
  },
29214
- state: ComponentStates.ENABLED,
29215
- };
29536
+ state: this._resetPasswordFormState,
29537
+ });
29216
29538
  }
29217
- // Resolved props with defaults
29218
- get config() {
29219
- return { ...LOGIN_DEFAULTS, ...this.props };
29539
+ set resetPasswordFormState(value) {
29540
+ this._resetPasswordFormState = value;
29541
+ }
29542
+ set resetFormSectionName(value) {
29543
+ this._resetFormSectionName = value;
29220
29544
  }
29221
29545
  // ==========================================
29222
29546
  // HANDLERS
@@ -29225,13 +29549,13 @@ class LoginComponent {
29225
29549
  const email = event.fields['email'];
29226
29550
  const password = event.fields['password'];
29227
29551
  if (!email || !password) {
29228
- this.showToast('Completa todos los campos.');
29552
+ this.showToast(this.t('completeAllFields'));
29229
29553
  return;
29230
29554
  }
29231
- this.loginFormProps.state = ComponentStates.WORKING;
29555
+ this._loginFormState = ComponentStates.WORKING;
29232
29556
  this.authService.signin({ email, password }).subscribe({
29233
29557
  next: () => {
29234
- this.loginFormProps.state = ComponentStates.ENABLED;
29558
+ this._loginFormState = ComponentStates.ENABLED;
29235
29559
  if (this.authService.mfaPending().required) {
29236
29560
  this.openMFAVerifyModal();
29237
29561
  return;
@@ -29239,7 +29563,7 @@ class LoginComponent {
29239
29563
  this.handleLoginSuccess();
29240
29564
  },
29241
29565
  error: (err) => {
29242
- this.loginFormProps.state = ComponentStates.ENABLED;
29566
+ this._loginFormState = ComponentStates.ENABLED;
29243
29567
  const errorCode = err?.code;
29244
29568
  if (errorCode === 'AUTHV2_EMAIL_NOT_VERIFIED') {
29245
29569
  this.openVerifyModal(email);
@@ -29284,18 +29608,18 @@ class LoginComponent {
29284
29608
  const email = event.fields['email'];
29285
29609
  const password = event.fields['password'];
29286
29610
  if (!name || !email || !password) {
29287
- this.showToast('Completa todos los campos.');
29611
+ this.showToast(this.t('completeAllFields'));
29288
29612
  return;
29289
29613
  }
29290
- this.registerFormProps.state = ComponentStates.WORKING;
29614
+ this._registerFormState = ComponentStates.WORKING;
29291
29615
  this.authService.signup({ name, email, password }).subscribe({
29292
29616
  next: () => {
29293
- this.registerFormProps.state = ComponentStates.ENABLED;
29617
+ this._registerFormState = ComponentStates.ENABLED;
29294
29618
  this.closeRegisterModal();
29295
29619
  this.openVerifyModal(email);
29296
29620
  },
29297
29621
  error: (err) => {
29298
- this.registerFormProps.state = ComponentStates.ENABLED;
29622
+ this._registerFormState = ComponentStates.ENABLED;
29299
29623
  this.handleError(err, 'signup');
29300
29624
  },
29301
29625
  });
@@ -29305,19 +29629,20 @@ class LoginComponent {
29305
29629
  // ==========================================
29306
29630
  openVerifyModal(email) {
29307
29631
  this.pendingVerificationEmail = email;
29308
- this.verifyFormProps.sections[0].name = `Ingresa el código enviado a ${email}`;
29309
- this.verifyFormProps.state = ComponentStates.ENABLED;
29632
+ this._verifyFormSectionName = `${this.t('verifyDescription').replace('.', '')} (${email})`;
29633
+ this._verifyFormState = ComponentStates.ENABLED;
29310
29634
  this.isVerifyModalOpen = true;
29311
29635
  this.startResendCooldown();
29312
29636
  }
29313
29637
  closeVerifyModal() {
29314
29638
  this.isVerifyModalOpen = false;
29315
29639
  this.pendingVerificationEmail = '';
29640
+ this._verifyFormSectionName = '';
29316
29641
  this.stopResendCooldown();
29317
29642
  }
29318
29643
  verifyHandler(event) {
29319
29644
  const code = event.fields['code'];
29320
- this.verifyFormProps.state = ComponentStates.WORKING;
29645
+ this._verifyFormState = ComponentStates.WORKING;
29321
29646
  this.authService
29322
29647
  .verifyEmail({
29323
29648
  email: this.pendingVerificationEmail,
@@ -29325,13 +29650,13 @@ class LoginComponent {
29325
29650
  })
29326
29651
  .subscribe({
29327
29652
  next: () => {
29328
- this.verifyFormProps.state = ComponentStates.ENABLED;
29329
- this.showToast('¡Email verificado! Bienvenido.');
29653
+ this._verifyFormState = ComponentStates.ENABLED;
29654
+ this.showToast(this.t('emailVerified'));
29330
29655
  this.closeVerifyModal();
29331
29656
  this.handleLoginSuccess();
29332
29657
  },
29333
29658
  error: (err) => {
29334
- this.verifyFormProps.state = ComponentStates.ENABLED;
29659
+ this._verifyFormState = ComponentStates.ENABLED;
29335
29660
  this.handleError(err, 'verify');
29336
29661
  },
29337
29662
  });
@@ -29346,7 +29671,7 @@ class LoginComponent {
29346
29671
  })
29347
29672
  .subscribe({
29348
29673
  next: () => {
29349
- this.showToast('Código reenviado. Revisa tu email.');
29674
+ this.showToast(this.t('codeSent'));
29350
29675
  this.startResendCooldown();
29351
29676
  },
29352
29677
  error: (err) => {
@@ -29359,30 +29684,25 @@ class LoginComponent {
29359
29684
  // ==========================================
29360
29685
  openMFAVerifyModal() {
29361
29686
  const method = this.authService.mfaPending().method;
29362
- this.mfaVerifyFormProps.sections[0].name =
29363
- method === 'TOTP'
29364
- ? 'Ingresa el código de tu app de autenticación'
29365
- : method === 'EMAIL'
29366
- ? 'Ingresa el código enviado a tu correo'
29367
- : 'Ingresa el código enviado a tu teléfono';
29368
- this.mfaVerifyFormProps.state = ComponentStates.ENABLED;
29687
+ this._mfaMethod = method;
29688
+ this._mfaVerifyFormState = ComponentStates.ENABLED;
29369
29689
  this.isMFAVerifyModalOpen = true;
29370
- this.onMFARequired.emit({ method: method });
29690
+ this.onMFARequired.emit({ method });
29371
29691
  }
29372
29692
  closeMFAVerifyModal() {
29373
29693
  this.isMFAVerifyModalOpen = false;
29374
29694
  }
29375
29695
  verifyMFAHandler(event) {
29376
29696
  const code = event.fields['code'];
29377
- this.mfaVerifyFormProps.state = ComponentStates.WORKING;
29697
+ this._mfaVerifyFormState = ComponentStates.WORKING;
29378
29698
  this.authService.verifyMFA(code).subscribe({
29379
29699
  next: () => {
29380
- this.mfaVerifyFormProps.state = ComponentStates.ENABLED;
29700
+ this._mfaVerifyFormState = ComponentStates.ENABLED;
29381
29701
  this.closeMFAVerifyModal();
29382
29702
  this.handleLoginSuccess(undefined, true);
29383
29703
  },
29384
29704
  error: (err) => {
29385
- this.mfaVerifyFormProps.state = ComponentStates.ENABLED;
29705
+ this._mfaVerifyFormState = ComponentStates.ENABLED;
29386
29706
  this.handleError(err, 'mfa');
29387
29707
  },
29388
29708
  });
@@ -29399,19 +29719,19 @@ class LoginComponent {
29399
29719
  forgotPasswordHandler(event) {
29400
29720
  const email = event.fields['email'];
29401
29721
  if (!email) {
29402
- this.showToast('Ingresa tu correo electrónico.');
29722
+ this.showToast(this.t('enterEmail'));
29403
29723
  return;
29404
29724
  }
29405
- this.forgotPasswordFormProps.state = ComponentStates.WORKING;
29725
+ this._forgotPasswordFormState = ComponentStates.WORKING;
29406
29726
  this.authService.forgotPassword({ email }).subscribe({
29407
29727
  next: () => {
29408
- this.forgotPasswordFormProps.state = ComponentStates.ENABLED;
29728
+ this._forgotPasswordFormState = ComponentStates.ENABLED;
29409
29729
  this.closeForgotPasswordModal();
29410
29730
  this.openResetPasswordModal(email);
29411
- this.showToast('Código enviado. Revisa tu email.');
29731
+ this.showToast(this.t('codeSent'));
29412
29732
  },
29413
29733
  error: (err) => {
29414
- this.forgotPasswordFormProps.state = ComponentStates.ENABLED;
29734
+ this._forgotPasswordFormState = ComponentStates.ENABLED;
29415
29735
  this.handleError(err, 'forgot');
29416
29736
  },
29417
29737
  });
@@ -29421,20 +29741,21 @@ class LoginComponent {
29421
29741
  // ==========================================
29422
29742
  openResetPasswordModal(email) {
29423
29743
  this.pendingResetEmail = email;
29424
- this.resetPasswordFormProps.sections[0].name = `Ingresa el código enviado a ${email}`;
29425
- this.resetPasswordFormProps.state = ComponentStates.ENABLED;
29744
+ this._resetFormSectionName = `${this.t('resetDescription').replace('.', '')} (${email})`;
29745
+ this._resetPasswordFormState = ComponentStates.ENABLED;
29426
29746
  this.isResetPasswordModalOpen = true;
29427
29747
  this.startResetResendCooldown();
29428
29748
  }
29429
29749
  closeResetPasswordModal() {
29430
29750
  this.isResetPasswordModalOpen = false;
29431
29751
  this.pendingResetEmail = '';
29752
+ this._resetFormSectionName = '';
29432
29753
  this.stopResetResendCooldown();
29433
29754
  }
29434
29755
  resetPasswordHandler(event) {
29435
29756
  const code = event.fields['code'];
29436
29757
  const newPassword = event.fields['newPassword'];
29437
- this.resetPasswordFormProps.state = ComponentStates.WORKING;
29758
+ this._resetPasswordFormState = ComponentStates.WORKING;
29438
29759
  this.authService
29439
29760
  .resetPassword({
29440
29761
  email: this.pendingResetEmail,
@@ -29443,12 +29764,12 @@ class LoginComponent {
29443
29764
  })
29444
29765
  .subscribe({
29445
29766
  next: () => {
29446
- this.resetPasswordFormProps.state = ComponentStates.ENABLED;
29447
- this.showToast('¡Contraseña actualizada! Ya puedes iniciar sesión.');
29767
+ this._resetPasswordFormState = ComponentStates.ENABLED;
29768
+ this.showToast(this.t('passwordUpdated'));
29448
29769
  this.closeResetPasswordModal();
29449
29770
  },
29450
29771
  error: (err) => {
29451
- this.resetPasswordFormProps.state = ComponentStates.ENABLED;
29772
+ this._resetPasswordFormState = ComponentStates.ENABLED;
29452
29773
  this.handleError(err, 'reset');
29453
29774
  },
29454
29775
  });
@@ -29463,7 +29784,7 @@ class LoginComponent {
29463
29784
  })
29464
29785
  .subscribe({
29465
29786
  next: () => {
29466
- this.showToast('Código reenviado. Revisa tu email.');
29787
+ this.showToast(this.t('codeSent'));
29467
29788
  this.startResetResendCooldown();
29468
29789
  },
29469
29790
  error: (err) => {
@@ -29475,7 +29796,7 @@ class LoginComponent {
29475
29796
  // HELPERS
29476
29797
  // ==========================================
29477
29798
  handleLoginSuccess(oauthProvider, mfaCompleted) {
29478
- this.showToast('¡Bienvenido!');
29799
+ this.showToast(this.t('welcome'));
29479
29800
  this.onSuccess.emit({
29480
29801
  user: this.authService.user(),
29481
29802
  mfaCompleted,
@@ -29504,55 +29825,34 @@ class LoginComponent {
29504
29825
  }
29505
29826
  getErrorMessage(err) {
29506
29827
  const error = err;
29507
- const errorMessages = {
29508
- // Validation
29509
- VALIDATION_MISSING_REQUIRED_FIELDS: 'Faltan campos requeridos',
29510
- VALIDATION_INVALID_REQUEST: 'Solicitud inválida',
29828
+ // Map error codes to i18n keys
29829
+ const errorKeyMap = {
29511
29830
  // Signin
29512
- AUTHV2_INVALID_CREDENTIALS: 'Correo o contraseña incorrectos',
29513
- AUTHV2_EMAIL_NOT_VERIFIED: 'Debes verificar tu correo electrónico',
29514
- AUTHV2_ACCOUNT_SUSPENDED: 'Tu cuenta ha sido suspendida',
29515
- AUTHV2_SIGNIN_FAILED: 'Error al iniciar sesión',
29831
+ AUTHV2_INVALID_CREDENTIALS: 'errorInvalidCredentials',
29832
+ AUTHV2_EMAIL_NOT_VERIFIED: 'errorEmailNotVerified',
29833
+ AUTHV2_ACCOUNT_SUSPENDED: 'errorAccountSuspended',
29516
29834
  // Signup
29517
- AUTHV2_EMAIL_EXISTS: 'Este correo ya está registrado',
29518
- AUTHV2_PHONE_EXISTS: 'Este teléfono ya está registrado',
29519
- AUTHV2_WEAK_PASSWORD: 'La contraseña es muy débil',
29520
- AUTHV2_SIGNUP_FAILED: 'Error al crear la cuenta',
29835
+ AUTHV2_EMAIL_EXISTS: 'errorEmailExists',
29836
+ AUTHV2_WEAK_PASSWORD: 'errorWeakPassword',
29521
29837
  // Verification codes
29522
- AUTHV2_INVALID_CODE: 'Código incorrecto',
29523
- AUTHV2_EXPIRED_CODE: 'El código ha expirado. Solicita uno nuevo.',
29524
- AUTHV2_CODE_EXPIRED: 'El código ha expirado. Solicita uno nuevo.',
29525
- AUTHV2_CODE_ALREADY_USED: 'Este código ya fue utilizado',
29526
- AUTHV2_TOO_MANY_ATTEMPTS: 'Demasiados intentos, intenta más tarde',
29527
- AUTHV2_SEND_CODE_FAILED: 'Error al enviar el código',
29528
- AUTHV2_ALREADY_VERIFIED: 'Este email ya está verificado',
29838
+ AUTHV2_INVALID_CODE: 'errorInvalidCode',
29839
+ AUTHV2_EXPIRED_CODE: 'errorExpiredCode',
29840
+ AUTHV2_CODE_EXPIRED: 'errorExpiredCode',
29841
+ AUTHV2_TOO_MANY_ATTEMPTS: 'errorTooManyAttempts',
29529
29842
  // MFA
29530
- AUTHV2_MFA_INVALID_CODE: 'Código de verificación incorrecto',
29531
- // Password reset
29532
- AUTHV2_PASSWORD_RESET_FAILED: 'Error al restablecer la contraseña',
29533
- AUTHV2_INVALID_CURRENT_PASSWORD: 'Contraseña actual incorrecta',
29534
- AUTHV2_SAME_PASSWORD: 'La nueva contraseña debe ser diferente',
29535
- // Session/Tokens
29536
- AUTHV2_INVALID_TOKEN: 'Sesión inválida',
29537
- AUTHV2_EXPIRED_TOKEN: 'Tu sesión ha expirado',
29538
- AUTHV2_SESSION_EXPIRED: 'Tu sesión ha expirado',
29539
- // User
29540
- AUTHV2_USER_NOT_FOUND: 'Usuario no encontrado',
29843
+ AUTHV2_MFA_INVALID_CODE: 'errorMFAInvalidCode',
29541
29844
  // OAuth
29542
- POPUP_BLOCKED: 'Por favor, permite ventanas emergentes para este sitio',
29543
- POPUP_CLOSED: 'Se canceló la autenticación',
29544
- INVALID_RESPONSE: 'Error en la respuesta del servidor',
29545
- OAUTH_FAILED: 'Error de autenticación',
29546
- OAUTH_EMAIL_EXISTS: 'Este correo ya está registrado con otro método',
29845
+ POPUP_BLOCKED: 'errorPopupBlocked',
29846
+ OAUTH_FAILED: 'errorOAuthFailed',
29547
29847
  };
29548
29848
  const errorCode = error?.error?.code || error?.code;
29549
- if (errorCode && errorMessages[errorCode]) {
29550
- return errorMessages[errorCode];
29849
+ if (errorCode && errorKeyMap[errorCode]) {
29850
+ return this.t(errorKeyMap[errorCode]);
29551
29851
  }
29552
29852
  if (error?.error?.message) {
29553
29853
  return error.error.message;
29554
29854
  }
29555
- return 'Ha ocurrido un error. Intenta de nuevo.';
29855
+ return this.t('errorGeneric');
29556
29856
  }
29557
29857
  // ==========================================
29558
29858
  // COOLDOWN TIMERS
@@ -29594,7 +29894,7 @@ class LoginComponent {
29594
29894
  this.stopResetResendCooldown();
29595
29895
  }
29596
29896
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: LoginComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
29597
- 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>o contin\u00FAa con</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) {\n @case ('google') {\n <ion-icon slot=\"start\" name=\"logo-google\"></ion-icon>\n {{ isOAuthLoading ? 'Conectando...' : 'Continuar con Google' }}\n }\n @case ('apple') {\n <ion-icon slot=\"start\" name=\"logo-apple\"></ion-icon>\n {{ isOAuthLoading ? 'Conectando...' : 'Continuar con Apple' }}\n }\n @case ('microsoft') {\n <ion-icon slot=\"start\" name=\"logo-microsoft\"></ion-icon>\n {{ isOAuthLoading ? 'Conectando...' : 'Continuar con Microsoft' }}\n }\n }\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 \u00BFNo tienes cuenta?\n <a (click)=\"openRegisterModal()\">Registrarse</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 \u00BFOlvidaste tu contrase\u00F1a?\n <a (click)=\"openForgotPasswordModal()\">Recuperar contrase\u00F1a</a>\n </ion-text>\n </div>\n }\n\n <!-- Legal Notice -->\n @if (props.legal) {\n <div class=\"legal-notice\">\n <ion-text color=\"medium\">\n <p>\n Utilizamos los servicios de\n @if (props.legal.companyLink) {\n <a [href]=\"props.legal.companyLink\"><strong>{{ props.legal.companyName }}</strong></a>\n } @else {\n <strong>{{ props.legal.companyName }}</strong>\n }\n para ofrecerte una experiencia segura. Al iniciar sesi\u00F3n, aceptas\n nuestros\n @if (props.legal.termsLink) {\n <a [href]=\"props.legal.termsLink\">T\u00E9rminos y Condiciones</a>\n } @else {\n <span>T\u00E9rminos y Condiciones</span>\n }\n y\n @if (props.legal.privacyLink) {\n <a [href]=\"props.legal.privacyLink\">Pol\u00EDtica de Privacidad</a>\n } @else {\n <span>Pol\u00EDtica de Privacidad</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\" (click)=\"closeRegisterModal()\">\n <ion-icon name=\"close-outline\"></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 \u00BFYa tienes cuenta?\n <a (click)=\"closeRegisterModal()\">Iniciar sesi\u00F3n</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\" (click)=\"closeVerifyModal()\">\n <ion-icon name=\"close-outline\"></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 \u00BFNo has recibido tu c\u00F3digo?\n @if (resendCooldown > 0) {\n <span class=\"cooldown\">Reenviar en {{ resendCooldown }}s</span>\n } @else {\n <a (click)=\"resendCode()\">Reenviar</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\" (click)=\"closeForgotPasswordModal()\">\n <ion-icon name=\"close-outline\"></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\" (click)=\"closeResetPasswordModal()\">\n <ion-icon name=\"close-outline\"></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 \u00BFNo has recibido tu c\u00F3digo?\n @if (resetResendCooldown > 0) {\n <span class=\"cooldown\">Reenviar en {{ resetResendCooldown }}s</span>\n } @else {\n <a (click)=\"resendResetCode()\">Reenviar</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\">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-buttons slot=\"end\">\n <ion-button fill=\"clear\" (click)=\"closeMFAVerifyModal()\">\n <ion-icon name=\"close-outline\"></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;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}.modal-form-section{padding:1rem}.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"] }] }); }
29897
+ 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) {\n @case ('google') {\n <ion-icon slot=\"start\" name=\"logo-google\"></ion-icon>\n {{ isOAuthLoading ? t('connecting') : t('continueWithGoogle') }}\n }\n @case ('apple') {\n <ion-icon slot=\"start\" name=\"logo-apple\"></ion-icon>\n {{ isOAuthLoading ? t('connecting') : t('continueWithApple') }}\n }\n @case ('microsoft') {\n <ion-icon slot=\"start\" name=\"logo-microsoft\"></ion-icon>\n {{ isOAuthLoading ? t('connecting') : t('continueWithMicrosoft') }}\n }\n }\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=\"medium\">\n <p>\n {{ t('legalPrefix') }}\n @if (props.legal.companyLink) {\n <a [href]=\"props.legal.companyLink\"><strong>{{ props.legal.companyName }}</strong></a>\n } @else {\n <strong>{{ props.legal.companyName }}</strong>\n }\n {{ t('legalSuffix') }}\n @if (props.legal.termsLink) {\n <a [href]=\"props.legal.termsLink\">{{ t('termsAndConditions') }}</a>\n } @else {\n <span>{{ t('termsAndConditions') }}</span>\n }\n {{ t('and') }}\n @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\" (click)=\"closeRegisterModal()\">\n <ion-icon name=\"close-outline\"></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\" (click)=\"closeVerifyModal()\">\n <ion-icon name=\"close-outline\"></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') }}\n @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\" (click)=\"closeForgotPasswordModal()\">\n <ion-icon name=\"close-outline\"></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\" (click)=\"closeResetPasswordModal()\">\n <ion-icon name=\"close-outline\"></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') }}\n @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\">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-buttons slot=\"end\">\n <ion-button fill=\"clear\" (click)=\"closeMFAVerifyModal()\">\n <ion-icon name=\"close-outline\"></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;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}.modal-form-section{padding:1rem}.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"] }] }); }
29598
29898
  }
29599
29899
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: LoginComponent, decorators: [{
29600
29900
  type: Component,
@@ -29610,7 +29910,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
29610
29910
  IonToolbar,
29611
29911
  FormComponent,
29612
29912
  ImageComponent,
29613
- ], 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>o contin\u00FAa con</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) {\n @case ('google') {\n <ion-icon slot=\"start\" name=\"logo-google\"></ion-icon>\n {{ isOAuthLoading ? 'Conectando...' : 'Continuar con Google' }}\n }\n @case ('apple') {\n <ion-icon slot=\"start\" name=\"logo-apple\"></ion-icon>\n {{ isOAuthLoading ? 'Conectando...' : 'Continuar con Apple' }}\n }\n @case ('microsoft') {\n <ion-icon slot=\"start\" name=\"logo-microsoft\"></ion-icon>\n {{ isOAuthLoading ? 'Conectando...' : 'Continuar con Microsoft' }}\n }\n }\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 \u00BFNo tienes cuenta?\n <a (click)=\"openRegisterModal()\">Registrarse</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 \u00BFOlvidaste tu contrase\u00F1a?\n <a (click)=\"openForgotPasswordModal()\">Recuperar contrase\u00F1a</a>\n </ion-text>\n </div>\n }\n\n <!-- Legal Notice -->\n @if (props.legal) {\n <div class=\"legal-notice\">\n <ion-text color=\"medium\">\n <p>\n Utilizamos los servicios de\n @if (props.legal.companyLink) {\n <a [href]=\"props.legal.companyLink\"><strong>{{ props.legal.companyName }}</strong></a>\n } @else {\n <strong>{{ props.legal.companyName }}</strong>\n }\n para ofrecerte una experiencia segura. Al iniciar sesi\u00F3n, aceptas\n nuestros\n @if (props.legal.termsLink) {\n <a [href]=\"props.legal.termsLink\">T\u00E9rminos y Condiciones</a>\n } @else {\n <span>T\u00E9rminos y Condiciones</span>\n }\n y\n @if (props.legal.privacyLink) {\n <a [href]=\"props.legal.privacyLink\">Pol\u00EDtica de Privacidad</a>\n } @else {\n <span>Pol\u00EDtica de Privacidad</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\" (click)=\"closeRegisterModal()\">\n <ion-icon name=\"close-outline\"></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 \u00BFYa tienes cuenta?\n <a (click)=\"closeRegisterModal()\">Iniciar sesi\u00F3n</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\" (click)=\"closeVerifyModal()\">\n <ion-icon name=\"close-outline\"></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 \u00BFNo has recibido tu c\u00F3digo?\n @if (resendCooldown > 0) {\n <span class=\"cooldown\">Reenviar en {{ resendCooldown }}s</span>\n } @else {\n <a (click)=\"resendCode()\">Reenviar</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\" (click)=\"closeForgotPasswordModal()\">\n <ion-icon name=\"close-outline\"></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\" (click)=\"closeResetPasswordModal()\">\n <ion-icon name=\"close-outline\"></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 \u00BFNo has recibido tu c\u00F3digo?\n @if (resetResendCooldown > 0) {\n <span class=\"cooldown\">Reenviar en {{ resetResendCooldown }}s</span>\n } @else {\n <a (click)=\"resendResetCode()\">Reenviar</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\">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-buttons slot=\"end\">\n <ion-button fill=\"clear\" (click)=\"closeMFAVerifyModal()\">\n <ion-icon name=\"close-outline\"></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;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}.modal-form-section{padding:1rem}.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"] }]
29913
+ ], 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) {\n @case ('google') {\n <ion-icon slot=\"start\" name=\"logo-google\"></ion-icon>\n {{ isOAuthLoading ? t('connecting') : t('continueWithGoogle') }}\n }\n @case ('apple') {\n <ion-icon slot=\"start\" name=\"logo-apple\"></ion-icon>\n {{ isOAuthLoading ? t('connecting') : t('continueWithApple') }}\n }\n @case ('microsoft') {\n <ion-icon slot=\"start\" name=\"logo-microsoft\"></ion-icon>\n {{ isOAuthLoading ? t('connecting') : t('continueWithMicrosoft') }}\n }\n }\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=\"medium\">\n <p>\n {{ t('legalPrefix') }}\n @if (props.legal.companyLink) {\n <a [href]=\"props.legal.companyLink\"><strong>{{ props.legal.companyName }}</strong></a>\n } @else {\n <strong>{{ props.legal.companyName }}</strong>\n }\n {{ t('legalSuffix') }}\n @if (props.legal.termsLink) {\n <a [href]=\"props.legal.termsLink\">{{ t('termsAndConditions') }}</a>\n } @else {\n <span>{{ t('termsAndConditions') }}</span>\n }\n {{ t('and') }}\n @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\" (click)=\"closeRegisterModal()\">\n <ion-icon name=\"close-outline\"></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\" (click)=\"closeVerifyModal()\">\n <ion-icon name=\"close-outline\"></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') }}\n @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\" (click)=\"closeForgotPasswordModal()\">\n <ion-icon name=\"close-outline\"></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\" (click)=\"closeResetPasswordModal()\">\n <ion-icon name=\"close-outline\"></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') }}\n @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\">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-buttons slot=\"end\">\n <ion-button fill=\"clear\" (click)=\"closeMFAVerifyModal()\">\n <ion-icon name=\"close-outline\"></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;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}.modal-form-section{padding:1rem}.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"] }]
29614
29914
  }], propDecorators: { props: [{
29615
29915
  type: Input
29616
29916
  }], onSuccess: [{
@@ -34936,5 +35236,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
34936
35236
  * Generated bundle index. Do not edit.
34937
35237
  */
34938
35238
 
34939
- export { AD_SIZE_MAP, API_TABLE_COLUMN_LABELS, ARTICLE_SPACING, AccordionComponent, ActionHeaderComponent, ActionType, AdSlotComponent, AdsLoaderService, AdsService, AlertBoxComponent, AnalyticsErrorHandler, AnalyticsRouterTracker, AnalyticsService, ArticleBuilder, ArticleComponent, AuthService, AuthStateService, AuthStorageService, AuthSyncService, AvatarComponent, BannerComponent, BaseDefault, BoxComponent, BreadcrumbComponent, ButtonComponent, ButtonGroupComponent, COMMON_COUNTRY_CODES, COMMON_CURRENCIES, CURRENCY_INFO, CardComponent, CardSection, CardType, CardsCarouselComponent, CheckInputComponent, ChipGroupComponent, ClearDefault, ClearDefaultBlock, ClearDefaultFull, ClearDefaultRound, ClearDefaultRoundBlock, ClearDefaultRoundFull, CodeDisplayComponent, CommandDisplayComponent, CommentComponent, CommentInputComponent, CommentSectionComponent, CompanyFooterComponent, ComponentStates, ConfirmationDialogService, ContentLoaderComponent, CountdownComponent, CurrencyInputComponent, DEFAULT_ADS_CONFIG, DEFAULT_AUTH_CONFIG, DEFAULT_CANCEL_BUTTON, DEFAULT_CONFIRM_BUTTON, DEFAULT_COUNTDOWN_LABELS, DEFAULT_COUNTDOWN_LABELS_EN, DEFAULT_EMPTY_STATE, DEFAULT_INFINITE_LIST_METADATA, DEFAULT_LEGEND_LABELS, DEFAULT_MODAL_CANCEL_BUTTON, DEFAULT_MODAL_CONFIRM_BUTTON, DEFAULT_PAGE_SIZE_OPTIONS, DEFAULT_PAYMENT_STATUS_COLORS, DEFAULT_PAYMENT_STATUS_LABELS, DEFAULT_PLATFORMS, DEFAULT_REFRESHER_METADATA, DEFAULT_SKELETON_CONFIG, DEFAULT_STATUS_COLORS, DEFAULT_STATUS_LABELS, DEFAULT_WINNER_LABELS, DataTableComponent, DateInputComponent, DateRangeInputComponent, DetailSkeletonComponent, DeviceService, DisplayComponent, DividerComponent, DocsApiTableComponent, DocsBreadcrumbComponent, DocsCalloutComponent, DocsCodeExampleComponent, DocsLayoutComponent, DocsNavLinksComponent, DocsSearchComponent, DocsSidebarComponent, DocsTocComponent, DownloadService, EmailInputComponent, ExpandableTextComponent, FabComponent, FileInputComponent, FirebaseService, FirestoreCollectionFactory, FirestoreService, FooterComponent, FooterLinksComponent, FormComponent, FormFooterComponent, FormSkeletonComponent, FunHeaderComponent, GlowCardComponent, GridSkeletonComponent, HeaderComponent, HintComponent, HorizontalScrollComponent, HourInputComponent, HrefComponent, I18nService, INITIAL_AUTH_STATE, INITIAL_MFA_STATE, Icon, IconComponent, IconService, ImageComponent, InAppBrowserService, InfiniteListComponent, InfoComponent, InputType, ItemListComponent, LANG_STORAGE_KEY$1 as LANG_STORAGE_KEY, LOGIN_DEFAULTS, LanguageSelectorComponent, LayeredCardComponent, LayoutComponent, LinkComponent, LinkProcessorService, LinksAccordionComponent, LinksCakeComponent, ListSkeletonComponent, LoadingDirective, LocalStorageService, LocaleService, LoginComponent, MODAL_SIZES, MOTION, MenuComponent, MessagingService, ModalService, MultiSelectSearchComponent, NavigationService, NoContentComponent, NotesBoxComponent, NotificationsService, NumberFromToComponent, NumberInputComponent, NumberStepperComponent, OAuthCallbackComponent, OAuthService, OutlineDefault, OutlineDefaultBlock, OutlineDefaultFull, OutlineDefaultRound, OutlineDefaultRoundBlock, OutlineDefaultRoundFull, PLATFORM_CONFIGS, PageContentComponent, PageTemplateComponent, PageWrapperComponent, PaginationComponent, PaginationService, ParticipantCardComponent, PasswordInputComponent, PhoneInputComponent, PillComponent, PinInputComponent, PlainCodeBoxComponent, PopoverSelectorComponent, PresetService, PriceTagComponent, PrimarySolidBlockButton, PrimarySolidBlockHrefButton, PrimarySolidBlockIconButton, PrimarySolidBlockIconHrefButton, PrimarySolidDefaultRoundButton, PrimarySolidDefaultRoundHrefButton, PrimarySolidDefaultRoundIconButton, PrimarySolidDefaultRoundIconHrefButton, PrimarySolidFullButton, PrimarySolidFullHrefButton, PrimarySolidFullIconButton, PrimarySolidFullIconHrefButton, PrimarySolidLargeRoundButton, PrimarySolidLargeRoundHrefButton, PrimarySolidLargeRoundIconButton, PrimarySolidLargeRoundIconHrefButton, PrimarySolidSmallRoundButton, PrimarySolidSmallRoundHrefButton, PrimarySolidSmallRoundIconButton, PrimarySolidSmallRoundIconHrefButton, ProcessLinksPipe, ProfileSkeletonComponent, ProgressBarComponent, ProgressRingComponent, ProgressStatusComponent, PrompterComponent, QR_PRESETS, QrCodeComponent, QrGeneratorService, QueryBuilder, QuoteBoxComponent, RadioInputComponent, RaffleStatusCardComponent, RangeInputComponent, RatingComponent, RecapCardComponent, RefresherComponent, RightsFooterComponent, SKELETON_PRESETS, SearchSelectorComponent, SearchbarComponent, SecondarySolidBlockButton, SecondarySolidBlockHrefButton, SecondarySolidBlockIconButton, SecondarySolidBlockIconHrefButton, SecondarySolidDefaultRoundButton, SecondarySolidDefaultRoundHrefButton, SecondarySolidDefaultRoundIconButton, SecondarySolidDefaultRoundIconHrefButton, SecondarySolidFullButton, SecondarySolidFullHrefButton, SecondarySolidFullIconButton, SecondarySolidFullIconHrefButton, SecondarySolidLargeRoundButton, SecondarySolidLargeRoundHrefButton, SecondarySolidLargeRoundIconButton, SecondarySolidLargeRoundIconHrefButton, SecondarySolidSmallRoundButton, SecondarySolidSmallRoundHrefButton, SecondarySolidSmallRoundIconButton, SecondarySolidSmallRoundIconHrefButton, SegmentControlComponent, SelectSearchComponent, SessionService, ShareButtonsComponent, SimpleComponent, SkeletonComponent, SkeletonService, SolidBlockButton, SolidDefault, SolidDefaultBlock, SolidDefaultButton, SolidDefaultFull, SolidDefaultRound, SolidDefaultRoundBlock, SolidDefaultRoundButton, SolidDefaultRoundFull, SolidFullButton, SolidLargeButton, SolidLargeRoundButton, SolidSmallButton, SolidSmallRoundButton, StatsCardComponent, StepperComponent, StorageService, SwipeCarouselComponent, TabbedContentComponent, TableSkeletonComponent, TabsComponent, TestimonialCardComponent, TestimonialCarouselComponent, TextComponent, TextInputComponent, TextareaInputComponent, ThemeOption, ThemeService, TicketGridComponent, TimelineComponent, TitleBlockComponent, TitleComponent, ToastService, ToggleInputComponent, TokenService, ToolbarActionType, ToolbarComponent, TranslatePipe, TypedCollection, VALTECH_ADS_CONFIG, VALTECH_AUTH_CONFIG, VALTECH_DEFAULT_CONTENT, VALTECH_FIREBASE_CONFIG, WinnerDisplayComponent, WizardComponent, WizardFooterComponent, applyDefaultValueToControl, authGuard, authInterceptor, buildPath, collections, createFirebaseConfig, createGlowCardProps, createInitialPaginationState, createNumberFromToField, createTitleProps, extractPathParams, getCollectionPath, getDocumentId, goToTop, guestGuard, hasEmulators, isAtEnd, isCollectionPath, isDocumentPath, isEmulatorMode, isValidPath, joinPath, maxLength, permissionGuard, permissionGuardFromRoute, provideValtechAds, provideValtechAuth, provideValtechAuthInterceptor, provideValtechFirebase, provideValtechI18n, provideValtechPresets, provideValtechSkeleton, query, replaceSpecialChars, resolveColor, resolveInputDefaultValue, roleGuard, storagePaths, superAdminGuard };
35239
+ export { AD_SIZE_MAP, API_TABLE_COLUMN_LABELS, ARTICLE_SPACING, AccordionComponent, ActionHeaderComponent, ActionType, AdSlotComponent, AdsLoaderService, AdsService, AlertBoxComponent, AnalyticsErrorHandler, AnalyticsRouterTracker, AnalyticsService, ArticleBuilder, ArticleComponent, AuthService, AuthStateService, AuthStorageService, AuthSyncService, AvatarComponent, BannerComponent, BaseDefault, BoxComponent, BreadcrumbComponent, ButtonComponent, ButtonGroupComponent, COMMON_COUNTRY_CODES, COMMON_CURRENCIES, CURRENCY_INFO, CardComponent, CardSection, CardType, CardsCarouselComponent, CheckInputComponent, ChipGroupComponent, ClearDefault, ClearDefaultBlock, ClearDefaultFull, ClearDefaultRound, ClearDefaultRoundBlock, ClearDefaultRoundFull, CodeDisplayComponent, CommandDisplayComponent, CommentComponent, CommentInputComponent, CommentSectionComponent, CompanyFooterComponent, ComponentStates, ConfirmationDialogService, ContentLoaderComponent, CountdownComponent, CurrencyInputComponent, DEFAULT_ADS_CONFIG, DEFAULT_AUTH_CONFIG, DEFAULT_CANCEL_BUTTON, DEFAULT_CONFIRM_BUTTON, DEFAULT_COUNTDOWN_LABELS, DEFAULT_COUNTDOWN_LABELS_EN, DEFAULT_EMPTY_STATE, DEFAULT_INFINITE_LIST_METADATA, DEFAULT_LEGEND_LABELS, DEFAULT_MODAL_CANCEL_BUTTON, DEFAULT_MODAL_CONFIRM_BUTTON, DEFAULT_PAGE_SIZE_OPTIONS, DEFAULT_PAYMENT_STATUS_COLORS, DEFAULT_PAYMENT_STATUS_LABELS, DEFAULT_PLATFORMS, DEFAULT_REFRESHER_METADATA, DEFAULT_SKELETON_CONFIG, DEFAULT_STATUS_COLORS, DEFAULT_STATUS_LABELS, DEFAULT_WINNER_LABELS, DataTableComponent, DateInputComponent, DateRangeInputComponent, DetailSkeletonComponent, DeviceService, DisplayComponent, DividerComponent, DocsApiTableComponent, DocsBreadcrumbComponent, DocsCalloutComponent, DocsCodeExampleComponent, DocsLayoutComponent, DocsNavLinksComponent, DocsSearchComponent, DocsSidebarComponent, DocsTocComponent, DownloadService, EmailInputComponent, ExpandableTextComponent, FabComponent, FileInputComponent, FirebaseService, FirestoreCollectionFactory, FirestoreService, FooterComponent, FooterLinksComponent, FormComponent, FormFooterComponent, FormSkeletonComponent, FunHeaderComponent, GlowCardComponent, GridSkeletonComponent, HeaderComponent, HintComponent, HorizontalScrollComponent, HourInputComponent, HrefComponent, I18nService, INITIAL_AUTH_STATE, INITIAL_MFA_STATE, Icon, IconComponent, IconService, ImageComponent, InAppBrowserService, InfiniteListComponent, InfoComponent, InputI18nHelper, InputType, ItemListComponent, LANG_STORAGE_KEY$1 as LANG_STORAGE_KEY, LOGIN_DEFAULTS, LanguageSelectorComponent, LayeredCardComponent, LayoutComponent, LinkComponent, LinkProcessorService, LinksAccordionComponent, LinksCakeComponent, ListSkeletonComponent, LoadingDirective, LocalStorageService, LocaleService, LoginComponent, MODAL_SIZES, MOTION, MenuComponent, MessagingService, ModalService, MultiSelectSearchComponent, NavigationService, NoContentComponent, NotesBoxComponent, NotificationsService, NumberFromToComponent, NumberInputComponent, NumberStepperComponent, OAuthCallbackComponent, OAuthService, OutlineDefault, OutlineDefaultBlock, OutlineDefaultFull, OutlineDefaultRound, OutlineDefaultRoundBlock, OutlineDefaultRoundFull, PLATFORM_CONFIGS, PageContentComponent, PageTemplateComponent, PageWrapperComponent, PaginationComponent, PaginationService, ParticipantCardComponent, PasswordInputComponent, PhoneInputComponent, PillComponent, PinInputComponent, PlainCodeBoxComponent, PopoverSelectorComponent, PresetService, PriceTagComponent, PrimarySolidBlockButton, PrimarySolidBlockHrefButton, PrimarySolidBlockIconButton, PrimarySolidBlockIconHrefButton, PrimarySolidDefaultRoundButton, PrimarySolidDefaultRoundHrefButton, PrimarySolidDefaultRoundIconButton, PrimarySolidDefaultRoundIconHrefButton, PrimarySolidFullButton, PrimarySolidFullHrefButton, PrimarySolidFullIconButton, PrimarySolidFullIconHrefButton, PrimarySolidLargeRoundButton, PrimarySolidLargeRoundHrefButton, PrimarySolidLargeRoundIconButton, PrimarySolidLargeRoundIconHrefButton, PrimarySolidSmallRoundButton, PrimarySolidSmallRoundHrefButton, PrimarySolidSmallRoundIconButton, PrimarySolidSmallRoundIconHrefButton, ProcessLinksPipe, ProfileSkeletonComponent, ProgressBarComponent, ProgressRingComponent, ProgressStatusComponent, PrompterComponent, QR_PRESETS, QrCodeComponent, QrGeneratorService, QueryBuilder, QuoteBoxComponent, RadioInputComponent, RaffleStatusCardComponent, RangeInputComponent, RatingComponent, RecapCardComponent, RefresherComponent, RightsFooterComponent, SKELETON_PRESETS, SearchSelectorComponent, SearchbarComponent, SecondarySolidBlockButton, SecondarySolidBlockHrefButton, SecondarySolidBlockIconButton, SecondarySolidBlockIconHrefButton, SecondarySolidDefaultRoundButton, SecondarySolidDefaultRoundHrefButton, SecondarySolidDefaultRoundIconButton, SecondarySolidDefaultRoundIconHrefButton, SecondarySolidFullButton, SecondarySolidFullHrefButton, SecondarySolidFullIconButton, SecondarySolidFullIconHrefButton, SecondarySolidLargeRoundButton, SecondarySolidLargeRoundHrefButton, SecondarySolidLargeRoundIconButton, SecondarySolidLargeRoundIconHrefButton, SecondarySolidSmallRoundButton, SecondarySolidSmallRoundHrefButton, SecondarySolidSmallRoundIconButton, SecondarySolidSmallRoundIconHrefButton, SegmentControlComponent, SelectSearchComponent, SessionService, ShareButtonsComponent, SimpleComponent, SkeletonComponent, SkeletonService, SolidBlockButton, SolidDefault, SolidDefaultBlock, SolidDefaultButton, SolidDefaultFull, SolidDefaultRound, SolidDefaultRoundBlock, SolidDefaultRoundButton, SolidDefaultRoundFull, SolidFullButton, SolidLargeButton, SolidLargeRoundButton, SolidSmallButton, SolidSmallRoundButton, StatsCardComponent, StepperComponent, StorageService, SwipeCarouselComponent, TabbedContentComponent, TableSkeletonComponent, TabsComponent, TestimonialCardComponent, TestimonialCarouselComponent, TextComponent, TextInputComponent, TextareaInputComponent, ThemeOption, ThemeService, TicketGridComponent, TimelineComponent, TitleBlockComponent, TitleComponent, ToastService, ToggleInputComponent, TokenService, ToolbarActionType, ToolbarComponent, TranslatePipe, TypedCollection, VALTECH_ADS_CONFIG, VALTECH_AUTH_CONFIG, VALTECH_DEFAULT_CONTENT, VALTECH_FIREBASE_CONFIG, WinnerDisplayComponent, WizardComponent, WizardFooterComponent, applyDefaultValueToControl, authGuard, authInterceptor, buildPath, collections, createFirebaseConfig, createGlowCardProps, createInitialPaginationState, createNumberFromToField, createTitleProps, extractPathParams, getCollectionPath, getDocumentId, goToTop, guestGuard, hasEmulators, isAtEnd, isCollectionPath, isDocumentPath, isEmulatorMode, isValidPath, joinPath, maxLength, permissionGuard, permissionGuardFromRoute, provideValtechAds, provideValtechAuth, provideValtechAuthInterceptor, provideValtechFirebase, provideValtechI18n, provideValtechPresets, provideValtechSkeleton, query, replaceSpecialChars, resolveColor, resolveInputDefaultValue, roleGuard, storagePaths, superAdminGuard };
34940
35240
  //# sourceMappingURL=valtech-components.mjs.map