@mintplayer/ng-spark-auth 0.0.3 → 0.0.5

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.
@@ -1,14 +1,16 @@
1
1
  import * as i0 from '@angular/core';
2
- import { InjectionToken, inject, signal, computed, Injectable, Pipe, makeEnvironmentProviders, Component } from '@angular/core';
3
- import { HttpClient, withInterceptors, withXsrfConfiguration } from '@angular/common/http';
4
- import { switchMap, tap } from 'rxjs';
2
+ import { InjectionToken, inject, signal, computed, Injectable, Pipe, makeEnvironmentProviders, ChangeDetectionStrategy, Component } from '@angular/core';
3
+ import { HttpClient, withInterceptors, withXsrfConfiguration, HttpErrorResponse } from '@angular/common/http';
4
+ import { firstValueFrom, tap } from 'rxjs';
5
5
  import { Router, RouterLink, ActivatedRoute } from '@angular/router';
6
6
  import * as i1 from '@angular/forms';
7
7
  import { FormBuilder, Validators, ReactiveFormsModule } from '@angular/forms';
8
- import * as i2 from '@mintplayer/ng-bootstrap/form';
9
- import { BsFormModule } from '@mintplayer/ng-bootstrap/form';
10
- import * as i3 from '@mintplayer/ng-bootstrap/toggle-button';
11
- import { BsToggleButtonModule } from '@mintplayer/ng-bootstrap/toggle-button';
8
+ import { Color } from '@mintplayer/ng-bootstrap';
9
+ import { BsAlertComponent } from '@mintplayer/ng-bootstrap/alert';
10
+ import { BsCardComponent, BsCardHeaderComponent } from '@mintplayer/ng-bootstrap/card';
11
+ import { BsFormComponent, BsFormControlDirective } from '@mintplayer/ng-bootstrap/form';
12
+ import { BsToggleButtonComponent } from '@mintplayer/ng-bootstrap/toggle-button';
13
+ import { BsSpinnerComponent } from '@mintplayer/ng-bootstrap/spinner';
12
14
 
13
15
  const SPARK_AUTH_CONFIG = new InjectionToken('SPARK_AUTH_CONFIG');
14
16
  const defaultSparkAuthConfig = {
@@ -26,52 +28,57 @@ class SparkAuthService {
26
28
  user = this.currentUser.asReadonly();
27
29
  isAuthenticated = computed(() => this.currentUser()?.isAuthenticated === true, ...(ngDevMode ? [{ debugName: "isAuthenticated" }] : []));
28
30
  constructor() {
29
- this.checkAuth().subscribe();
31
+ this.checkAuth();
30
32
  }
31
- login(email, password) {
32
- return this.http
33
- .post(`${this.config.apiBasePath}/login?useCookies=true`, { email, password })
34
- .pipe(switchMap(() => this.csrfRefresh()), tap(() => { this.checkAuth().subscribe(); }));
33
+ async login(email, password) {
34
+ await firstValueFrom(this.http.post(`${this.config.apiBasePath}/login?useCookies=true`, { email, password }));
35
+ await this.csrfRefresh();
36
+ await this.checkAuth();
35
37
  }
36
- loginTwoFactor(twoFactorCode, twoFactorRecoveryCode) {
37
- return this.http
38
- .post(`${this.config.apiBasePath}/login?useCookies=true`, {
38
+ async loginTwoFactor(twoFactorCode, twoFactorRecoveryCode) {
39
+ await firstValueFrom(this.http.post(`${this.config.apiBasePath}/login?useCookies=true`, {
39
40
  twoFactorCode,
40
41
  twoFactorRecoveryCode,
41
- })
42
- .pipe(switchMap(() => this.csrfRefresh()), tap(() => { this.checkAuth().subscribe(); }));
42
+ }));
43
+ await this.csrfRefresh();
44
+ await this.checkAuth();
43
45
  }
44
- register(email, password) {
45
- return this.http.post(`${this.config.apiBasePath}/register`, { email, password });
46
+ async register(email, password) {
47
+ await firstValueFrom(this.http.post(`${this.config.apiBasePath}/register`, { email, password }));
46
48
  }
47
- logout() {
48
- return this.http
49
- .post(`${this.config.apiBasePath}/logout`, {})
50
- .pipe(switchMap(() => this.csrfRefresh()), tap(() => { this.currentUser.set(null); }));
49
+ async logout() {
50
+ await firstValueFrom(this.http.post(`${this.config.apiBasePath}/logout`, {}));
51
+ await this.csrfRefresh();
52
+ this.currentUser.set(null);
51
53
  }
52
- csrfRefresh() {
53
- return this.http.post(`${this.config.apiBasePath}/csrf-refresh`, {});
54
+ async csrfRefresh() {
55
+ await firstValueFrom(this.http.post(`${this.config.apiBasePath}/csrf-refresh`, {}));
54
56
  }
55
- checkAuth() {
56
- return this.http.get(`${this.config.apiBasePath}/me`).pipe(tap({
57
- next: (user) => this.currentUser.set(user),
58
- error: () => this.currentUser.set(null),
59
- }));
57
+ async checkAuth() {
58
+ try {
59
+ const user = await firstValueFrom(this.http.get(`${this.config.apiBasePath}/me`));
60
+ this.currentUser.set(user);
61
+ return user;
62
+ }
63
+ catch {
64
+ this.currentUser.set(null);
65
+ return null;
66
+ }
60
67
  }
61
- forgotPassword(email) {
62
- return this.http.post(`${this.config.apiBasePath}/forgotPassword`, { email });
68
+ async forgotPassword(email) {
69
+ await firstValueFrom(this.http.post(`${this.config.apiBasePath}/forgotPassword`, { email }));
63
70
  }
64
- resetPassword(email, resetCode, newPassword) {
65
- return this.http.post(`${this.config.apiBasePath}/resetPassword`, {
71
+ async resetPassword(email, resetCode, newPassword) {
72
+ await firstValueFrom(this.http.post(`${this.config.apiBasePath}/resetPassword`, {
66
73
  email,
67
74
  resetCode,
68
75
  newPassword,
69
- });
76
+ }));
70
77
  }
71
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: SparkAuthService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
72
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: SparkAuthService, providedIn: 'root' });
78
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SparkAuthService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
79
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SparkAuthService, providedIn: 'root' });
73
80
  }
74
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: SparkAuthService, decorators: [{
81
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SparkAuthService, decorators: [{
75
82
  type: Injectable,
76
83
  args: [{ providedIn: 'root' }]
77
84
  }], ctorParameters: () => [] });
@@ -80,9 +87,16 @@ class SparkAuthTranslationService {
80
87
  http = inject(HttpClient);
81
88
  translationsMap = signal({}, ...(ngDevMode ? [{ debugName: "translationsMap" }] : []));
82
89
  constructor() {
83
- this.http.get('/spark/translations').subscribe(t => {
90
+ this.loadTranslations();
91
+ }
92
+ async loadTranslations() {
93
+ try {
94
+ const t = await firstValueFrom(this.http.get('/spark/translations'));
84
95
  this.translationsMap.set(t);
85
- });
96
+ }
97
+ catch {
98
+ // Translations failed to load; keys will be shown as-is
99
+ }
86
100
  }
87
101
  t(key) {
88
102
  const ts = this.translationsMap()[key];
@@ -91,10 +105,10 @@ class SparkAuthTranslationService {
91
105
  const lang = localStorage.getItem('spark-lang') ?? navigator.language?.split('-')[0] ?? 'en';
92
106
  return ts[lang] ?? ts['en'] ?? Object.values(ts)[0] ?? key;
93
107
  }
94
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: SparkAuthTranslationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
95
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: SparkAuthTranslationService, providedIn: 'root' });
108
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SparkAuthTranslationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
109
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SparkAuthTranslationService, providedIn: 'root' });
96
110
  }
97
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: SparkAuthTranslationService, decorators: [{
111
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SparkAuthTranslationService, decorators: [{
98
112
  type: Injectable,
99
113
  args: [{ providedIn: 'root' }]
100
114
  }], ctorParameters: () => [] });
@@ -104,10 +118,10 @@ class TranslateKeyPipe {
104
118
  transform(key) {
105
119
  return this.translation.t(key);
106
120
  }
107
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: TranslateKeyPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
108
- static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.1.5", ngImport: i0, type: TranslateKeyPipe, isStandalone: true, name: "t", pure: false });
121
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TranslateKeyPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
122
+ static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.0.6", ngImport: i0, type: TranslateKeyPipe, isStandalone: true, name: "t", pure: false });
109
123
  }
110
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: TranslateKeyPipe, decorators: [{
124
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TranslateKeyPipe, decorators: [{
111
125
  type: Pipe,
112
126
  args: [{ name: 't', pure: false, standalone: true }]
113
127
  }] });
@@ -202,36 +216,16 @@ class SparkAuthBarComponent {
202
216
  authService = inject(SparkAuthService);
203
217
  config = inject(SPARK_AUTH_CONFIG);
204
218
  router = inject(Router);
205
- onLogout() {
206
- this.authService.logout().subscribe(() => {
207
- this.router.navigateByUrl('/');
208
- });
209
- }
210
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: SparkAuthBarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
211
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.5", type: SparkAuthBarComponent, isStandalone: true, selector: "spark-auth-bar", ngImport: i0, template: `
212
- @if (authService.isAuthenticated()) {
213
- <span class="me-2">{{ authService.user()?.userName }}</span>
214
- <button class="btn btn-outline-light btn-sm" (click)="onLogout()">{{ 'authLogout' | t }}</button>
215
- } @else {
216
- <a class="btn btn-outline-light btn-sm" [routerLink]="config.loginUrl">{{ 'authLogin' | t }}</a>
219
+ async onLogout() {
220
+ await this.authService.logout();
221
+ this.router.navigateByUrl('/');
217
222
  }
218
- `, isInline: true, dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "pipe", type: TranslateKeyPipe, name: "t" }] });
223
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SparkAuthBarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
224
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: SparkAuthBarComponent, isStandalone: true, selector: "spark-auth-bar", ngImport: i0, template: "@if (authService.isAuthenticated()) {\n <span class=\"me-2\">{{ authService.user()?.userName }}</span>\n <button class=\"btn btn-outline-light btn-sm\" (click)=\"onLogout()\">{{ 'authLogout' | t }}</button>\n} @else {\n <a class=\"btn btn-outline-light btn-sm\" [routerLink]=\"config.loginUrl\">{{ 'authLogin' | t }}</a>\n}\n", dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "pipe", type: TranslateKeyPipe, name: "t" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
219
225
  }
220
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: SparkAuthBarComponent, decorators: [{
226
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SparkAuthBarComponent, decorators: [{
221
227
  type: Component,
222
- args: [{
223
- selector: 'spark-auth-bar',
224
- standalone: true,
225
- imports: [RouterLink, TranslateKeyPipe],
226
- template: `
227
- @if (authService.isAuthenticated()) {
228
- <span class="me-2">{{ authService.user()?.userName }}</span>
229
- <button class="btn btn-outline-light btn-sm" (click)="onLogout()">{{ 'authLogout' | t }}</button>
230
- } @else {
231
- <a class="btn btn-outline-light btn-sm" [routerLink]="config.loginUrl">{{ 'authLogin' | t }}</a>
232
- }
233
- `,
234
- }]
228
+ args: [{ selector: 'spark-auth-bar', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [RouterLink, TranslateKeyPipe], template: "@if (authService.isAuthenticated()) {\n <span class=\"me-2\">{{ authService.user()?.userName }}</span>\n <button class=\"btn btn-outline-light btn-sm\" (click)=\"onLogout()\">{{ 'authLogout' | t }}</button>\n} @else {\n <a class=\"btn btn-outline-light btn-sm\" [routerLink]=\"config.loginUrl\">{{ 'authLogin' | t }}</a>\n}\n" }]
235
229
  }] });
236
230
 
237
231
  class SparkLoginComponent {
@@ -242,6 +236,7 @@ class SparkLoginComponent {
242
236
  config = inject(SPARK_AUTH_CONFIG);
243
237
  translation = inject(SparkAuthTranslationService);
244
238
  routePaths = inject(SPARK_AUTH_ROUTE_PATHS);
239
+ colors = Color;
245
240
  loading = signal(false, ...(ngDevMode ? [{ debugName: "loading" }] : []));
246
241
  errorMessage = signal('', ...(ngDevMode ? [{ debugName: "errorMessage" }] : []));
247
242
  form = this.fb.group({
@@ -249,159 +244,38 @@ class SparkLoginComponent {
249
244
  password: ['', Validators.required],
250
245
  rememberMe: [false],
251
246
  });
252
- onSubmit() {
247
+ async onSubmit() {
253
248
  if (this.form.invalid)
254
249
  return;
255
250
  this.loading.set(true);
256
251
  this.errorMessage.set('');
257
252
  const { email, password } = this.form.value;
258
- this.authService.login(email, password).subscribe({
259
- next: () => {
260
- this.loading.set(false);
253
+ try {
254
+ await this.authService.login(email, password);
255
+ const returnUrl = this.route.snapshot.queryParamMap.get('returnUrl');
256
+ this.router.navigateByUrl(returnUrl || this.config.defaultRedirectUrl);
257
+ }
258
+ catch (err) {
259
+ if (err instanceof HttpErrorResponse && err.status === 401 && err.error?.detail === 'RequiresTwoFactor') {
261
260
  const returnUrl = this.route.snapshot.queryParamMap.get('returnUrl');
262
- this.router.navigateByUrl(returnUrl || this.config.defaultRedirectUrl);
263
- },
264
- error: (err) => {
265
- this.loading.set(false);
266
- if (err.status === 401 && err.error?.detail === 'RequiresTwoFactor') {
267
- const returnUrl = this.route.snapshot.queryParamMap.get('returnUrl');
268
- this.router.navigate([this.routePaths.twoFactor], {
269
- queryParams: returnUrl ? { returnUrl } : undefined,
270
- });
271
- }
272
- else {
273
- this.errorMessage.set(this.translation.t('authInvalidCredentials'));
274
- }
275
- },
276
- });
261
+ this.router.navigate([this.routePaths.twoFactor], {
262
+ queryParams: returnUrl ? { returnUrl } : undefined,
263
+ });
264
+ }
265
+ else {
266
+ this.errorMessage.set(this.translation.t('authInvalidCredentials'));
267
+ }
268
+ }
269
+ finally {
270
+ this.loading.set(false);
271
+ }
277
272
  }
278
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: SparkLoginComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
279
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.5", type: SparkLoginComponent, isStandalone: true, selector: "spark-login", ngImport: i0, template: `
280
- <div class="d-flex justify-content-center">
281
- <div class="card" style="width: 100%; max-width: 400px;">
282
- <div class="card-body">
283
- <h3 class="card-title text-center mb-4">{{ 'authLogin' | t }}</h3>
284
-
285
- @if (errorMessage()) {
286
- <div class="alert alert-danger" role="alert">{{ errorMessage() }}</div>
287
- }
288
-
289
- <bs-form>
290
- <form [formGroup]="form" (ngSubmit)="onSubmit()">
291
- <div class="mb-3">
292
- <label for="email" class="form-label">{{ 'authEmail' | t }}</label>
293
- <input
294
- type="text"
295
- id="email"
296
- formControlName="email"
297
- autocomplete="username"
298
- />
299
- </div>
300
-
301
- <div class="mb-3">
302
- <label for="password" class="form-label">{{ 'authPassword' | t }}</label>
303
- <input
304
- type="password"
305
- id="password"
306
- formControlName="password"
307
- autocomplete="current-password"
308
- />
309
- </div>
310
-
311
- <div class="mb-3">
312
- <bs-toggle-button [type]="'checkbox'" formControlName="rememberMe" [name]="'rememberMe'">{{ 'authRememberMe' | t }}</bs-toggle-button>
313
- </div>
314
-
315
- <button
316
- type="submit"
317
- class="btn btn-primary w-100"
318
- [disabled]="loading()"
319
- >
320
- @if (loading()) {
321
- <span class="spinner-border spinner-border-sm me-1" role="status"></span>
322
- }
323
- {{ 'authLogin' | t }}
324
- </button>
325
- </form>
326
- </bs-form>
327
-
328
- <div class="mt-3 text-center">
329
- <a [routerLink]="routePaths.register">{{ 'authCreateAccount' | t }}</a>
330
- </div>
331
- <div class="mt-2 text-center">
332
- <a [routerLink]="routePaths.forgotPassword">{{ 'authForgotPassword' | t }}</a>
333
- </div>
334
- </div>
335
- </div>
336
- </div>
337
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: BsFormModule }, { kind: "component", type: i2.BsFormComponent, selector: "bs-form", inputs: ["action", "method"], outputs: ["submitted"] }, { kind: "directive", type: i2.BsFormControlDirective, selector: "bs-form input:not(.no-form-control), bs-form textarea:not(.no-form-control)" }, { kind: "ngmodule", type: BsToggleButtonModule }, { kind: "component", type: i3.BsToggleButtonComponent, selector: "bs-toggle-button", inputs: ["type", "isToggled", "name", "value", "group"], outputs: ["isToggledChange"] }, { kind: "directive", type: i3.BsToggleButtonValueAccessor, selector: "bs-toggle-button" }, { kind: "pipe", type: TranslateKeyPipe, name: "t" }] });
273
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SparkLoginComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
274
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: SparkLoginComponent, isStandalone: true, selector: "spark-login", ngImport: i0, template: "<div class=\"d-flex justify-content-center\">\n <bs-card style=\"width: 100%; max-width: 400px;\">\n <bs-card-header>\n <h3 class=\"mb-0 text-center\">{{ 'authLogin' | t }}</h3>\n </bs-card-header>\n <div class=\"p-4\">\n @if (errorMessage()) {\n <bs-alert [type]=\"colors.danger\" class=\"mb-3\">{{ errorMessage() }}</bs-alert>\n }\n\n <bs-form>\n <form [formGroup]=\"form\" (ngSubmit)=\"onSubmit()\">\n <div class=\"mb-3\">\n <label for=\"email\" class=\"form-label\">{{ 'authEmail' | t }}</label>\n <input\n type=\"text\"\n id=\"email\"\n formControlName=\"email\"\n autocomplete=\"username\"\n />\n </div>\n\n <div class=\"mb-3\">\n <label for=\"password\" class=\"form-label\">{{ 'authPassword' | t }}</label>\n <input\n type=\"password\"\n id=\"password\"\n formControlName=\"password\"\n autocomplete=\"current-password\"\n />\n </div>\n\n <div class=\"mb-3\">\n <bs-toggle-button [type]=\"'checkbox'\" formControlName=\"rememberMe\" [name]=\"'rememberMe'\">{{ 'authRememberMe' | t }}</bs-toggle-button>\n </div>\n\n <button\n type=\"submit\"\n class=\"btn btn-primary w-100\"\n [disabled]=\"loading()\"\n >\n @if (loading()) {\n <bs-spinner class=\"me-1\" />\n }\n {{ 'authLogin' | t }}\n </button>\n </form>\n </bs-form>\n\n <div class=\"mt-3 text-center\">\n <a [routerLink]=\"routePaths.register\">{{ 'authCreateAccount' | t }}</a>\n </div>\n <div class=\"mt-2 text-center\">\n <a [routerLink]=\"routePaths.forgotPassword\">{{ 'authForgotPassword' | t }}</a>\n </div>\n </div>\n </bs-card>\n</div>\n", dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: BsAlertComponent, selector: "bs-alert", inputs: ["type", "isVisible"], outputs: ["isVisibleChange", "afterOpenedOrClosed"] }, { kind: "component", type: BsCardComponent, selector: "bs-card", inputs: ["rounded"] }, { kind: "component", type: BsCardHeaderComponent, selector: "bs-card-header", inputs: ["noPadding"] }, { kind: "component", type: BsFormComponent, selector: "bs-form", inputs: ["action", "method"], outputs: ["submitted"] }, { kind: "directive", type: BsFormControlDirective, selector: "bs-form input:not(.no-form-control), bs-form textarea:not(.no-form-control)" }, { kind: "component", type: BsToggleButtonComponent, selector: "bs-toggle-button", inputs: ["type", "isToggled", "name", "value", "group"], outputs: ["isToggledChange"] }, { kind: "component", type: BsSpinnerComponent, selector: "bs-spinner", inputs: ["type", "color"] }, { kind: "pipe", type: TranslateKeyPipe, name: "t" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
338
275
  }
339
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: SparkLoginComponent, decorators: [{
276
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SparkLoginComponent, decorators: [{
340
277
  type: Component,
341
- args: [{
342
- selector: 'spark-login',
343
- standalone: true,
344
- imports: [ReactiveFormsModule, RouterLink, BsFormModule, BsToggleButtonModule, TranslateKeyPipe],
345
- template: `
346
- <div class="d-flex justify-content-center">
347
- <div class="card" style="width: 100%; max-width: 400px;">
348
- <div class="card-body">
349
- <h3 class="card-title text-center mb-4">{{ 'authLogin' | t }}</h3>
350
-
351
- @if (errorMessage()) {
352
- <div class="alert alert-danger" role="alert">{{ errorMessage() }}</div>
353
- }
354
-
355
- <bs-form>
356
- <form [formGroup]="form" (ngSubmit)="onSubmit()">
357
- <div class="mb-3">
358
- <label for="email" class="form-label">{{ 'authEmail' | t }}</label>
359
- <input
360
- type="text"
361
- id="email"
362
- formControlName="email"
363
- autocomplete="username"
364
- />
365
- </div>
366
-
367
- <div class="mb-3">
368
- <label for="password" class="form-label">{{ 'authPassword' | t }}</label>
369
- <input
370
- type="password"
371
- id="password"
372
- formControlName="password"
373
- autocomplete="current-password"
374
- />
375
- </div>
376
-
377
- <div class="mb-3">
378
- <bs-toggle-button [type]="'checkbox'" formControlName="rememberMe" [name]="'rememberMe'">{{ 'authRememberMe' | t }}</bs-toggle-button>
379
- </div>
380
-
381
- <button
382
- type="submit"
383
- class="btn btn-primary w-100"
384
- [disabled]="loading()"
385
- >
386
- @if (loading()) {
387
- <span class="spinner-border spinner-border-sm me-1" role="status"></span>
388
- }
389
- {{ 'authLogin' | t }}
390
- </button>
391
- </form>
392
- </bs-form>
393
-
394
- <div class="mt-3 text-center">
395
- <a [routerLink]="routePaths.register">{{ 'authCreateAccount' | t }}</a>
396
- </div>
397
- <div class="mt-2 text-center">
398
- <a [routerLink]="routePaths.forgotPassword">{{ 'authForgotPassword' | t }}</a>
399
- </div>
400
- </div>
401
- </div>
402
- </div>
403
- `,
404
- }]
278
+ args: [{ selector: 'spark-login', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [ReactiveFormsModule, RouterLink, BsAlertComponent, BsCardComponent, BsCardHeaderComponent, BsFormComponent, BsFormControlDirective, BsToggleButtonComponent, BsSpinnerComponent, TranslateKeyPipe], template: "<div class=\"d-flex justify-content-center\">\n <bs-card style=\"width: 100%; max-width: 400px;\">\n <bs-card-header>\n <h3 class=\"mb-0 text-center\">{{ 'authLogin' | t }}</h3>\n </bs-card-header>\n <div class=\"p-4\">\n @if (errorMessage()) {\n <bs-alert [type]=\"colors.danger\" class=\"mb-3\">{{ errorMessage() }}</bs-alert>\n }\n\n <bs-form>\n <form [formGroup]=\"form\" (ngSubmit)=\"onSubmit()\">\n <div class=\"mb-3\">\n <label for=\"email\" class=\"form-label\">{{ 'authEmail' | t }}</label>\n <input\n type=\"text\"\n id=\"email\"\n formControlName=\"email\"\n autocomplete=\"username\"\n />\n </div>\n\n <div class=\"mb-3\">\n <label for=\"password\" class=\"form-label\">{{ 'authPassword' | t }}</label>\n <input\n type=\"password\"\n id=\"password\"\n formControlName=\"password\"\n autocomplete=\"current-password\"\n />\n </div>\n\n <div class=\"mb-3\">\n <bs-toggle-button [type]=\"'checkbox'\" formControlName=\"rememberMe\" [name]=\"'rememberMe'\">{{ 'authRememberMe' | t }}</bs-toggle-button>\n </div>\n\n <button\n type=\"submit\"\n class=\"btn btn-primary w-100\"\n [disabled]=\"loading()\"\n >\n @if (loading()) {\n <bs-spinner class=\"me-1\" />\n }\n {{ 'authLogin' | t }}\n </button>\n </form>\n </bs-form>\n\n <div class=\"mt-3 text-center\">\n <a [routerLink]=\"routePaths.register\">{{ 'authCreateAccount' | t }}</a>\n </div>\n <div class=\"mt-2 text-center\">\n <a [routerLink]=\"routePaths.forgotPassword\">{{ 'authForgotPassword' | t }}</a>\n </div>\n </div>\n </bs-card>\n</div>\n" }]
405
279
  }] });
406
280
 
407
281
  var sparkLogin_component = /*#__PURE__*/Object.freeze({
@@ -418,6 +292,7 @@ class SparkTwoFactorComponent {
418
292
  config = inject(SPARK_AUTH_CONFIG);
419
293
  translation = inject(SparkAuthTranslationService);
420
294
  routePaths = inject(SPARK_AUTH_ROUTE_PATHS);
295
+ colors = Color;
421
296
  loading = signal(false, ...(ngDevMode ? [{ debugName: "loading" }] : []));
422
297
  errorMessage = signal('', ...(ngDevMode ? [{ debugName: "errorMessage" }] : []));
423
298
  useRecoveryCode = signal(false, ...(ngDevMode ? [{ debugName: "useRecoveryCode" }] : []));
@@ -429,7 +304,7 @@ class SparkTwoFactorComponent {
429
304
  this.useRecoveryCode.update(v => !v);
430
305
  this.errorMessage.set('');
431
306
  }
432
- onSubmit() {
307
+ async onSubmit() {
433
308
  const isRecovery = this.useRecoveryCode();
434
309
  const code = isRecovery ? this.form.value.recoveryCode : this.form.value.code;
435
310
  if (!code?.trim()) {
@@ -442,159 +317,24 @@ class SparkTwoFactorComponent {
442
317
  this.errorMessage.set('');
443
318
  const twoFactorCode = isRecovery ? undefined : code;
444
319
  const twoFactorRecoveryCode = isRecovery ? code : undefined;
445
- this.authService.loginTwoFactor(twoFactorCode ?? '', twoFactorRecoveryCode).subscribe({
446
- next: () => {
447
- this.loading.set(false);
448
- const returnUrl = this.route.snapshot.queryParamMap.get('returnUrl');
449
- this.router.navigateByUrl(returnUrl || this.config.defaultRedirectUrl);
450
- },
451
- error: () => {
452
- this.loading.set(false);
453
- this.errorMessage.set(this.translation.t('authInvalidCode'));
454
- },
455
- });
320
+ try {
321
+ await this.authService.loginTwoFactor(twoFactorCode ?? '', twoFactorRecoveryCode);
322
+ const returnUrl = this.route.snapshot.queryParamMap.get('returnUrl');
323
+ this.router.navigateByUrl(returnUrl || this.config.defaultRedirectUrl);
324
+ }
325
+ catch {
326
+ this.errorMessage.set(this.translation.t('authInvalidCode'));
327
+ }
328
+ finally {
329
+ this.loading.set(false);
330
+ }
456
331
  }
457
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: SparkTwoFactorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
458
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.5", type: SparkTwoFactorComponent, isStandalone: true, selector: "spark-two-factor", ngImport: i0, template: `
459
- <div class="d-flex justify-content-center">
460
- <div class="card" style="width: 100%; max-width: 400px;">
461
- <div class="card-body">
462
- <h3 class="card-title text-center mb-4">{{ 'authTwoFactorTitle' | t }}</h3>
463
-
464
- @if (errorMessage()) {
465
- <div class="alert alert-danger" role="alert">{{ errorMessage() }}</div>
466
- }
467
-
468
- <bs-form>
469
- <form [formGroup]="form" (ngSubmit)="onSubmit()">
470
- @if (!useRecoveryCode()) {
471
- <div class="mb-3">
472
- <label for="code" class="form-label">{{ 'authCode' | t }}</label>
473
- <input
474
- type="text"
475
- id="code"
476
- formControlName="code"
477
- autocomplete="one-time-code"
478
- maxlength="6"
479
- [placeholder]="'authEnterCode' | t"
480
- />
481
- </div>
482
- } @else {
483
- <div class="mb-3">
484
- <label for="recoveryCode" class="form-label">{{ 'authRecoveryCode' | t }}</label>
485
- <input
486
- type="text"
487
- id="recoveryCode"
488
- formControlName="recoveryCode"
489
- autocomplete="off"
490
- [placeholder]="'authEnterRecoveryCode' | t"
491
- />
492
- </div>
493
- }
494
-
495
- <button
496
- type="submit"
497
- class="btn btn-primary w-100"
498
- [disabled]="loading()"
499
- >
500
- @if (loading()) {
501
- <span class="spinner-border spinner-border-sm me-1" role="status"></span>
502
- }
503
- {{ 'authVerify' | t }}
504
- </button>
505
- </form>
506
- </bs-form>
507
-
508
- <div class="mt-3 text-center">
509
- <button class="btn btn-link" (click)="toggleRecoveryCode()">
510
- @if (useRecoveryCode()) {
511
- {{ 'authUseAuthCode' | t }}
512
- } @else {
513
- {{ 'authUseRecoveryCode' | t }}
514
- }
515
- </button>
516
- </div>
517
- <div class="mt-2 text-center">
518
- <a [routerLink]="routePaths.login">{{ 'authBackToLogin' | t }}</a>
519
- </div>
520
- </div>
521
- </div>
522
- </div>
523
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: BsFormModule }, { kind: "component", type: i2.BsFormComponent, selector: "bs-form", inputs: ["action", "method"], outputs: ["submitted"] }, { kind: "directive", type: i2.BsFormControlDirective, selector: "bs-form input:not(.no-form-control), bs-form textarea:not(.no-form-control)" }, { kind: "pipe", type: TranslateKeyPipe, name: "t" }] });
332
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SparkTwoFactorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
333
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: SparkTwoFactorComponent, isStandalone: true, selector: "spark-two-factor", ngImport: i0, template: "<div class=\"d-flex justify-content-center\">\n <bs-card style=\"width: 100%; max-width: 400px;\">\n <bs-card-header>\n <h3 class=\"mb-0 text-center\">{{ 'authTwoFactorTitle' | t }}</h3>\n </bs-card-header>\n <div class=\"p-4\">\n @if (errorMessage()) {\n <bs-alert [type]=\"colors.danger\" class=\"mb-3\">{{ errorMessage() }}</bs-alert>\n }\n\n <bs-form>\n <form [formGroup]=\"form\" (ngSubmit)=\"onSubmit()\">\n @if (!useRecoveryCode()) {\n <div class=\"mb-3\">\n <label for=\"code\" class=\"form-label\">{{ 'authCode' | t }}</label>\n <input\n type=\"text\"\n id=\"code\"\n formControlName=\"code\"\n autocomplete=\"one-time-code\"\n maxlength=\"6\"\n [placeholder]=\"'authEnterCode' | t\"\n />\n </div>\n } @else {\n <div class=\"mb-3\">\n <label for=\"recoveryCode\" class=\"form-label\">{{ 'authRecoveryCode' | t }}</label>\n <input\n type=\"text\"\n id=\"recoveryCode\"\n formControlName=\"recoveryCode\"\n autocomplete=\"off\"\n [placeholder]=\"'authEnterRecoveryCode' | t\"\n />\n </div>\n }\n\n <button\n type=\"submit\"\n class=\"btn btn-primary w-100\"\n [disabled]=\"loading()\"\n >\n @if (loading()) {\n <bs-spinner class=\"me-1\" />\n }\n {{ 'authVerify' | t }}\n </button>\n </form>\n </bs-form>\n\n <div class=\"mt-3 text-center\">\n <button class=\"btn btn-link\" (click)=\"toggleRecoveryCode()\">\n @if (useRecoveryCode()) {\n {{ 'authUseAuthCode' | t }}\n } @else {\n {{ 'authUseRecoveryCode' | t }}\n }\n </button>\n </div>\n <div class=\"mt-2 text-center\">\n <a [routerLink]=\"routePaths.login\">{{ 'authBackToLogin' | t }}</a>\n </div>\n </div>\n </bs-card>\n</div>\n", dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: BsAlertComponent, selector: "bs-alert", inputs: ["type", "isVisible"], outputs: ["isVisibleChange", "afterOpenedOrClosed"] }, { kind: "component", type: BsCardComponent, selector: "bs-card", inputs: ["rounded"] }, { kind: "component", type: BsCardHeaderComponent, selector: "bs-card-header", inputs: ["noPadding"] }, { kind: "component", type: BsFormComponent, selector: "bs-form", inputs: ["action", "method"], outputs: ["submitted"] }, { kind: "directive", type: BsFormControlDirective, selector: "bs-form input:not(.no-form-control), bs-form textarea:not(.no-form-control)" }, { kind: "component", type: BsSpinnerComponent, selector: "bs-spinner", inputs: ["type", "color"] }, { kind: "pipe", type: TranslateKeyPipe, name: "t" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
524
334
  }
525
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: SparkTwoFactorComponent, decorators: [{
335
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SparkTwoFactorComponent, decorators: [{
526
336
  type: Component,
527
- args: [{
528
- selector: 'spark-two-factor',
529
- standalone: true,
530
- imports: [ReactiveFormsModule, RouterLink, BsFormModule, TranslateKeyPipe],
531
- template: `
532
- <div class="d-flex justify-content-center">
533
- <div class="card" style="width: 100%; max-width: 400px;">
534
- <div class="card-body">
535
- <h3 class="card-title text-center mb-4">{{ 'authTwoFactorTitle' | t }}</h3>
536
-
537
- @if (errorMessage()) {
538
- <div class="alert alert-danger" role="alert">{{ errorMessage() }}</div>
539
- }
540
-
541
- <bs-form>
542
- <form [formGroup]="form" (ngSubmit)="onSubmit()">
543
- @if (!useRecoveryCode()) {
544
- <div class="mb-3">
545
- <label for="code" class="form-label">{{ 'authCode' | t }}</label>
546
- <input
547
- type="text"
548
- id="code"
549
- formControlName="code"
550
- autocomplete="one-time-code"
551
- maxlength="6"
552
- [placeholder]="'authEnterCode' | t"
553
- />
554
- </div>
555
- } @else {
556
- <div class="mb-3">
557
- <label for="recoveryCode" class="form-label">{{ 'authRecoveryCode' | t }}</label>
558
- <input
559
- type="text"
560
- id="recoveryCode"
561
- formControlName="recoveryCode"
562
- autocomplete="off"
563
- [placeholder]="'authEnterRecoveryCode' | t"
564
- />
565
- </div>
566
- }
567
-
568
- <button
569
- type="submit"
570
- class="btn btn-primary w-100"
571
- [disabled]="loading()"
572
- >
573
- @if (loading()) {
574
- <span class="spinner-border spinner-border-sm me-1" role="status"></span>
575
- }
576
- {{ 'authVerify' | t }}
577
- </button>
578
- </form>
579
- </bs-form>
580
-
581
- <div class="mt-3 text-center">
582
- <button class="btn btn-link" (click)="toggleRecoveryCode()">
583
- @if (useRecoveryCode()) {
584
- {{ 'authUseAuthCode' | t }}
585
- } @else {
586
- {{ 'authUseRecoveryCode' | t }}
587
- }
588
- </button>
589
- </div>
590
- <div class="mt-2 text-center">
591
- <a [routerLink]="routePaths.login">{{ 'authBackToLogin' | t }}</a>
592
- </div>
593
- </div>
594
- </div>
595
- </div>
596
- `,
597
- }]
337
+ args: [{ selector: 'spark-two-factor', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [ReactiveFormsModule, RouterLink, BsAlertComponent, BsCardComponent, BsCardHeaderComponent, BsFormComponent, BsFormControlDirective, BsSpinnerComponent, TranslateKeyPipe], template: "<div class=\"d-flex justify-content-center\">\n <bs-card style=\"width: 100%; max-width: 400px;\">\n <bs-card-header>\n <h3 class=\"mb-0 text-center\">{{ 'authTwoFactorTitle' | t }}</h3>\n </bs-card-header>\n <div class=\"p-4\">\n @if (errorMessage()) {\n <bs-alert [type]=\"colors.danger\" class=\"mb-3\">{{ errorMessage() }}</bs-alert>\n }\n\n <bs-form>\n <form [formGroup]=\"form\" (ngSubmit)=\"onSubmit()\">\n @if (!useRecoveryCode()) {\n <div class=\"mb-3\">\n <label for=\"code\" class=\"form-label\">{{ 'authCode' | t }}</label>\n <input\n type=\"text\"\n id=\"code\"\n formControlName=\"code\"\n autocomplete=\"one-time-code\"\n maxlength=\"6\"\n [placeholder]=\"'authEnterCode' | t\"\n />\n </div>\n } @else {\n <div class=\"mb-3\">\n <label for=\"recoveryCode\" class=\"form-label\">{{ 'authRecoveryCode' | t }}</label>\n <input\n type=\"text\"\n id=\"recoveryCode\"\n formControlName=\"recoveryCode\"\n autocomplete=\"off\"\n [placeholder]=\"'authEnterRecoveryCode' | t\"\n />\n </div>\n }\n\n <button\n type=\"submit\"\n class=\"btn btn-primary w-100\"\n [disabled]=\"loading()\"\n >\n @if (loading()) {\n <bs-spinner class=\"me-1\" />\n }\n {{ 'authVerify' | t }}\n </button>\n </form>\n </bs-form>\n\n <div class=\"mt-3 text-center\">\n <button class=\"btn btn-link\" (click)=\"toggleRecoveryCode()\">\n @if (useRecoveryCode()) {\n {{ 'authUseAuthCode' | t }}\n } @else {\n {{ 'authUseRecoveryCode' | t }}\n }\n </button>\n </div>\n <div class=\"mt-2 text-center\">\n <a [routerLink]=\"routePaths.login\">{{ 'authBackToLogin' | t }}</a>\n </div>\n </div>\n </bs-card>\n</div>\n" }]
598
338
  }] });
599
339
 
600
340
  var sparkTwoFactor_component = /*#__PURE__*/Object.freeze({
@@ -617,6 +357,7 @@ class SparkRegisterComponent {
617
357
  router = inject(Router);
618
358
  translation = inject(SparkAuthTranslationService);
619
359
  routePaths = inject(SPARK_AUTH_ROUTE_PATHS);
360
+ colors = Color;
620
361
  loading = signal(false, ...(ngDevMode ? [{ debugName: "loading" }] : []));
621
362
  errorMessage = signal('', ...(ngDevMode ? [{ debugName: "errorMessage" }] : []));
622
363
  form = this.fb.group({
@@ -624,7 +365,7 @@ class SparkRegisterComponent {
624
365
  password: ['', Validators.required],
625
366
  confirmPassword: ['', Validators.required],
626
367
  }, { validators: passwordMatchValidator$1 });
627
- onSubmit() {
368
+ async onSubmit() {
628
369
  if (this.form.invalid) {
629
370
  this.form.markAllAsTouched();
630
371
  return;
@@ -632,15 +373,14 @@ class SparkRegisterComponent {
632
373
  this.loading.set(true);
633
374
  this.errorMessage.set('');
634
375
  const { email, password } = this.form.value;
635
- this.authService.register(email, password).subscribe({
636
- next: () => {
637
- this.loading.set(false);
638
- this.router.navigate([this.routePaths.login], {
639
- queryParams: { registered: 'true' },
640
- });
641
- },
642
- error: (err) => {
643
- this.loading.set(false);
376
+ try {
377
+ await this.authService.register(email, password);
378
+ this.router.navigate([this.routePaths.login], {
379
+ queryParams: { registered: 'true' },
380
+ });
381
+ }
382
+ catch (err) {
383
+ if (err instanceof HttpErrorResponse) {
644
384
  if (err.status === 400 && err.error?.errors) {
645
385
  const messages = [].concat(...Object.values(err.error.errors));
646
386
  this.errorMessage.set(messages.join(' '));
@@ -651,156 +391,21 @@ class SparkRegisterComponent {
651
391
  else {
652
392
  this.errorMessage.set(this.translation.t('authRegistrationFailed'));
653
393
  }
654
- },
655
- });
394
+ }
395
+ else {
396
+ this.errorMessage.set(this.translation.t('authRegistrationFailed'));
397
+ }
398
+ }
399
+ finally {
400
+ this.loading.set(false);
401
+ }
656
402
  }
657
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: SparkRegisterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
658
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.5", type: SparkRegisterComponent, isStandalone: true, selector: "spark-register", ngImport: i0, template: `
659
- <div class="d-flex justify-content-center">
660
- <div class="card" style="width: 100%; max-width: 400px;">
661
- <div class="card-body">
662
- <h3 class="card-title text-center mb-4">{{ 'authRegister' | t }}</h3>
663
-
664
- @if (errorMessage()) {
665
- <div class="alert alert-danger" role="alert">{{ errorMessage() }}</div>
666
- }
667
-
668
- <bs-form>
669
- <form [formGroup]="form" (ngSubmit)="onSubmit()">
670
- <div class="mb-3">
671
- <label for="email" class="form-label">{{ 'authEmail' | t }}</label>
672
- <input
673
- type="email"
674
- id="email"
675
- formControlName="email"
676
- autocomplete="email"
677
- />
678
- @if (form.get('email')?.touched && form.get('email')?.hasError('email')) {
679
- <div class="text-danger mt-1">{{ 'authInvalidEmail' | t }}</div>
680
- }
681
- </div>
682
-
683
- <div class="mb-3">
684
- <label for="password" class="form-label">{{ 'authPassword' | t }}</label>
685
- <input
686
- type="password"
687
- id="password"
688
- formControlName="password"
689
- autocomplete="new-password"
690
- />
691
- </div>
692
-
693
- <div class="mb-3">
694
- <label for="confirmPassword" class="form-label">{{ 'authConfirmPassword' | t }}</label>
695
- <input
696
- type="password"
697
- id="confirmPassword"
698
- formControlName="confirmPassword"
699
- autocomplete="new-password"
700
- />
701
- @if (form.touched && form.hasError('passwordMismatch')) {
702
- <div class="text-danger mt-1">{{ 'authPasswordMismatch' | t }}</div>
703
- }
704
- </div>
705
-
706
- <button
707
- type="submit"
708
- class="btn btn-primary w-100"
709
- [disabled]="loading()"
710
- >
711
- @if (loading()) {
712
- <span class="spinner-border spinner-border-sm me-1" role="status"></span>
713
- }
714
- {{ 'authRegister' | t }}
715
- </button>
716
- </form>
717
- </bs-form>
718
-
719
- <div class="mt-3 text-center">
720
- <span>{{ 'authAlreadyHaveAccount' | t }} </span>
721
- <a [routerLink]="routePaths.login">{{ 'authLogin' | t }}</a>
722
- </div>
723
- </div>
724
- </div>
725
- </div>
726
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: BsFormModule }, { kind: "component", type: i2.BsFormComponent, selector: "bs-form", inputs: ["action", "method"], outputs: ["submitted"] }, { kind: "directive", type: i2.BsFormControlDirective, selector: "bs-form input:not(.no-form-control), bs-form textarea:not(.no-form-control)" }, { kind: "pipe", type: TranslateKeyPipe, name: "t" }] });
403
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SparkRegisterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
404
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: SparkRegisterComponent, isStandalone: true, selector: "spark-register", ngImport: i0, template: "<div class=\"d-flex justify-content-center\">\n <bs-card style=\"width: 100%; max-width: 400px;\">\n <bs-card-header>\n <h3 class=\"mb-0 text-center\">{{ 'authRegister' | t }}</h3>\n </bs-card-header>\n <div class=\"p-4\">\n @if (errorMessage()) {\n <bs-alert [type]=\"colors.danger\" class=\"mb-3\">{{ errorMessage() }}</bs-alert>\n }\n\n <bs-form>\n <form [formGroup]=\"form\" (ngSubmit)=\"onSubmit()\">\n <div class=\"mb-3\">\n <label for=\"email\" class=\"form-label\">{{ 'authEmail' | t }}</label>\n <input\n type=\"email\"\n id=\"email\"\n formControlName=\"email\"\n autocomplete=\"email\"\n />\n @if (form.get('email')?.touched && form.get('email')?.hasError('email')) {\n <div class=\"text-danger mt-1\">{{ 'authInvalidEmail' | t }}</div>\n }\n </div>\n\n <div class=\"mb-3\">\n <label for=\"password\" class=\"form-label\">{{ 'authPassword' | t }}</label>\n <input\n type=\"password\"\n id=\"password\"\n formControlName=\"password\"\n autocomplete=\"new-password\"\n />\n </div>\n\n <div class=\"mb-3\">\n <label for=\"confirmPassword\" class=\"form-label\">{{ 'authConfirmPassword' | t }}</label>\n <input\n type=\"password\"\n id=\"confirmPassword\"\n formControlName=\"confirmPassword\"\n autocomplete=\"new-password\"\n />\n @if (form.touched && form.hasError('passwordMismatch')) {\n <div class=\"text-danger mt-1\">{{ 'authPasswordMismatch' | t }}</div>\n }\n </div>\n\n <button\n type=\"submit\"\n class=\"btn btn-primary w-100\"\n [disabled]=\"loading()\"\n >\n @if (loading()) {\n <bs-spinner class=\"me-1\" />\n }\n {{ 'authRegister' | t }}\n </button>\n </form>\n </bs-form>\n\n <div class=\"mt-3 text-center\">\n <span>{{ 'authAlreadyHaveAccount' | t }} </span>\n <a [routerLink]=\"routePaths.login\">{{ 'authLogin' | t }}</a>\n </div>\n </div>\n </bs-card>\n</div>\n", dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: BsAlertComponent, selector: "bs-alert", inputs: ["type", "isVisible"], outputs: ["isVisibleChange", "afterOpenedOrClosed"] }, { kind: "component", type: BsCardComponent, selector: "bs-card", inputs: ["rounded"] }, { kind: "component", type: BsCardHeaderComponent, selector: "bs-card-header", inputs: ["noPadding"] }, { kind: "component", type: BsFormComponent, selector: "bs-form", inputs: ["action", "method"], outputs: ["submitted"] }, { kind: "directive", type: BsFormControlDirective, selector: "bs-form input:not(.no-form-control), bs-form textarea:not(.no-form-control)" }, { kind: "component", type: BsSpinnerComponent, selector: "bs-spinner", inputs: ["type", "color"] }, { kind: "pipe", type: TranslateKeyPipe, name: "t" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
727
405
  }
728
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: SparkRegisterComponent, decorators: [{
406
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SparkRegisterComponent, decorators: [{
729
407
  type: Component,
730
- args: [{
731
- selector: 'spark-register',
732
- standalone: true,
733
- imports: [ReactiveFormsModule, RouterLink, BsFormModule, TranslateKeyPipe],
734
- template: `
735
- <div class="d-flex justify-content-center">
736
- <div class="card" style="width: 100%; max-width: 400px;">
737
- <div class="card-body">
738
- <h3 class="card-title text-center mb-4">{{ 'authRegister' | t }}</h3>
739
-
740
- @if (errorMessage()) {
741
- <div class="alert alert-danger" role="alert">{{ errorMessage() }}</div>
742
- }
743
-
744
- <bs-form>
745
- <form [formGroup]="form" (ngSubmit)="onSubmit()">
746
- <div class="mb-3">
747
- <label for="email" class="form-label">{{ 'authEmail' | t }}</label>
748
- <input
749
- type="email"
750
- id="email"
751
- formControlName="email"
752
- autocomplete="email"
753
- />
754
- @if (form.get('email')?.touched && form.get('email')?.hasError('email')) {
755
- <div class="text-danger mt-1">{{ 'authInvalidEmail' | t }}</div>
756
- }
757
- </div>
758
-
759
- <div class="mb-3">
760
- <label for="password" class="form-label">{{ 'authPassword' | t }}</label>
761
- <input
762
- type="password"
763
- id="password"
764
- formControlName="password"
765
- autocomplete="new-password"
766
- />
767
- </div>
768
-
769
- <div class="mb-3">
770
- <label for="confirmPassword" class="form-label">{{ 'authConfirmPassword' | t }}</label>
771
- <input
772
- type="password"
773
- id="confirmPassword"
774
- formControlName="confirmPassword"
775
- autocomplete="new-password"
776
- />
777
- @if (form.touched && form.hasError('passwordMismatch')) {
778
- <div class="text-danger mt-1">{{ 'authPasswordMismatch' | t }}</div>
779
- }
780
- </div>
781
-
782
- <button
783
- type="submit"
784
- class="btn btn-primary w-100"
785
- [disabled]="loading()"
786
- >
787
- @if (loading()) {
788
- <span class="spinner-border spinner-border-sm me-1" role="status"></span>
789
- }
790
- {{ 'authRegister' | t }}
791
- </button>
792
- </form>
793
- </bs-form>
794
-
795
- <div class="mt-3 text-center">
796
- <span>{{ 'authAlreadyHaveAccount' | t }} </span>
797
- <a [routerLink]="routePaths.login">{{ 'authLogin' | t }}</a>
798
- </div>
799
- </div>
800
- </div>
801
- </div>
802
- `,
803
- }]
408
+ args: [{ selector: 'spark-register', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [ReactiveFormsModule, RouterLink, BsAlertComponent, BsCardComponent, BsCardHeaderComponent, BsFormComponent, BsFormControlDirective, BsSpinnerComponent, TranslateKeyPipe], template: "<div class=\"d-flex justify-content-center\">\n <bs-card style=\"width: 100%; max-width: 400px;\">\n <bs-card-header>\n <h3 class=\"mb-0 text-center\">{{ 'authRegister' | t }}</h3>\n </bs-card-header>\n <div class=\"p-4\">\n @if (errorMessage()) {\n <bs-alert [type]=\"colors.danger\" class=\"mb-3\">{{ errorMessage() }}</bs-alert>\n }\n\n <bs-form>\n <form [formGroup]=\"form\" (ngSubmit)=\"onSubmit()\">\n <div class=\"mb-3\">\n <label for=\"email\" class=\"form-label\">{{ 'authEmail' | t }}</label>\n <input\n type=\"email\"\n id=\"email\"\n formControlName=\"email\"\n autocomplete=\"email\"\n />\n @if (form.get('email')?.touched && form.get('email')?.hasError('email')) {\n <div class=\"text-danger mt-1\">{{ 'authInvalidEmail' | t }}</div>\n }\n </div>\n\n <div class=\"mb-3\">\n <label for=\"password\" class=\"form-label\">{{ 'authPassword' | t }}</label>\n <input\n type=\"password\"\n id=\"password\"\n formControlName=\"password\"\n autocomplete=\"new-password\"\n />\n </div>\n\n <div class=\"mb-3\">\n <label for=\"confirmPassword\" class=\"form-label\">{{ 'authConfirmPassword' | t }}</label>\n <input\n type=\"password\"\n id=\"confirmPassword\"\n formControlName=\"confirmPassword\"\n autocomplete=\"new-password\"\n />\n @if (form.touched && form.hasError('passwordMismatch')) {\n <div class=\"text-danger mt-1\">{{ 'authPasswordMismatch' | t }}</div>\n }\n </div>\n\n <button\n type=\"submit\"\n class=\"btn btn-primary w-100\"\n [disabled]=\"loading()\"\n >\n @if (loading()) {\n <bs-spinner class=\"me-1\" />\n }\n {{ 'authRegister' | t }}\n </button>\n </form>\n </bs-form>\n\n <div class=\"mt-3 text-center\">\n <span>{{ 'authAlreadyHaveAccount' | t }} </span>\n <a [routerLink]=\"routePaths.login\">{{ 'authLogin' | t }}</a>\n </div>\n </div>\n </bs-card>\n</div>\n" }]
804
409
  }] });
805
410
 
806
411
  var sparkRegister_component = /*#__PURE__*/Object.freeze({
@@ -814,13 +419,14 @@ class SparkForgotPasswordComponent {
814
419
  authService = inject(SparkAuthService);
815
420
  translation = inject(SparkAuthTranslationService);
816
421
  routePaths = inject(SPARK_AUTH_ROUTE_PATHS);
422
+ colors = Color;
817
423
  loading = signal(false, ...(ngDevMode ? [{ debugName: "loading" }] : []));
818
424
  errorMessage = signal('', ...(ngDevMode ? [{ debugName: "errorMessage" }] : []));
819
425
  successMessage = signal('', ...(ngDevMode ? [{ debugName: "successMessage" }] : []));
820
426
  form = this.fb.group({
821
427
  email: ['', [Validators.required, Validators.email]],
822
428
  });
823
- onSubmit() {
429
+ async onSubmit() {
824
430
  if (this.form.invalid) {
825
431
  this.form.markAllAsTouched();
826
432
  return;
@@ -829,131 +435,21 @@ class SparkForgotPasswordComponent {
829
435
  this.errorMessage.set('');
830
436
  this.successMessage.set('');
831
437
  const { email } = this.form.value;
832
- this.authService.forgotPassword(email).subscribe({
833
- next: () => {
834
- this.loading.set(false);
835
- this.successMessage.set(this.translation.t('authForgotPasswordSuccess'));
836
- },
837
- error: () => {
838
- this.loading.set(false);
839
- // Don't reveal whether the email exists
840
- this.successMessage.set(this.translation.t('authForgotPasswordSuccess'));
841
- },
842
- });
438
+ try {
439
+ await this.authService.forgotPassword(email);
440
+ }
441
+ catch {
442
+ // Don't reveal whether the email exists
443
+ }
444
+ this.successMessage.set(this.translation.t('authForgotPasswordSuccess'));
445
+ this.loading.set(false);
843
446
  }
844
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: SparkForgotPasswordComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
845
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.5", type: SparkForgotPasswordComponent, isStandalone: true, selector: "spark-forgot-password", ngImport: i0, template: `
846
- <div class="d-flex justify-content-center">
847
- <div class="card" style="width: 100%; max-width: 400px;">
848
- <div class="card-body">
849
- <h3 class="card-title text-center mb-4">{{ 'authForgotPasswordTitle' | t }}</h3>
850
-
851
- @if (successMessage()) {
852
- <div class="alert alert-success" role="alert">{{ successMessage() }}</div>
853
- }
854
-
855
- @if (errorMessage()) {
856
- <div class="alert alert-danger" role="alert">{{ errorMessage() }}</div>
857
- }
858
-
859
- @if (!successMessage()) {
860
- <p class="text-muted mb-3">
861
- {{ 'authForgotPasswordDescription' | t }}
862
- </p>
863
-
864
- <bs-form>
865
- <form [formGroup]="form" (ngSubmit)="onSubmit()">
866
- <div class="mb-3">
867
- <label for="email" class="form-label">{{ 'authEmail' | t }}</label>
868
- <input
869
- type="email"
870
- id="email"
871
- formControlName="email"
872
- autocomplete="email"
873
- />
874
- </div>
875
-
876
- <button
877
- type="submit"
878
- class="btn btn-primary w-100"
879
- [disabled]="loading()"
880
- >
881
- @if (loading()) {
882
- <span class="spinner-border spinner-border-sm me-1" role="status"></span>
883
- }
884
- {{ 'authSendResetLink' | t }}
885
- </button>
886
- </form>
887
- </bs-form>
888
- }
889
-
890
- <div class="mt-3 text-center">
891
- <a [routerLink]="routePaths.login">{{ 'authBackToLogin' | t }}</a>
892
- </div>
893
- </div>
894
- </div>
895
- </div>
896
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: BsFormModule }, { kind: "component", type: i2.BsFormComponent, selector: "bs-form", inputs: ["action", "method"], outputs: ["submitted"] }, { kind: "directive", type: i2.BsFormControlDirective, selector: "bs-form input:not(.no-form-control), bs-form textarea:not(.no-form-control)" }, { kind: "pipe", type: TranslateKeyPipe, name: "t" }] });
447
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SparkForgotPasswordComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
448
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: SparkForgotPasswordComponent, isStandalone: true, selector: "spark-forgot-password", ngImport: i0, template: "<div class=\"d-flex justify-content-center\">\n <bs-card style=\"width: 100%; max-width: 400px;\">\n <bs-card-header>\n <h3 class=\"mb-0 text-center\">{{ 'authForgotPasswordTitle' | t }}</h3>\n </bs-card-header>\n <div class=\"p-4\">\n @if (successMessage()) {\n <bs-alert [type]=\"colors.success\" class=\"mb-3\">{{ successMessage() }}</bs-alert>\n }\n\n @if (errorMessage()) {\n <bs-alert [type]=\"colors.danger\" class=\"mb-3\">{{ errorMessage() }}</bs-alert>\n }\n\n @if (!successMessage()) {\n <p class=\"text-muted mb-3\">\n {{ 'authForgotPasswordDescription' | t }}\n </p>\n\n <bs-form>\n <form [formGroup]=\"form\" (ngSubmit)=\"onSubmit()\">\n <div class=\"mb-3\">\n <label for=\"email\" class=\"form-label\">{{ 'authEmail' | t }}</label>\n <input\n type=\"email\"\n id=\"email\"\n formControlName=\"email\"\n autocomplete=\"email\"\n />\n </div>\n\n <button\n type=\"submit\"\n class=\"btn btn-primary w-100\"\n [disabled]=\"loading()\"\n >\n @if (loading()) {\n <bs-spinner class=\"me-1\" />\n }\n {{ 'authSendResetLink' | t }}\n </button>\n </form>\n </bs-form>\n }\n\n <div class=\"mt-3 text-center\">\n <a [routerLink]=\"routePaths.login\">{{ 'authBackToLogin' | t }}</a>\n </div>\n </div>\n </bs-card>\n</div>\n", dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: BsAlertComponent, selector: "bs-alert", inputs: ["type", "isVisible"], outputs: ["isVisibleChange", "afterOpenedOrClosed"] }, { kind: "component", type: BsCardComponent, selector: "bs-card", inputs: ["rounded"] }, { kind: "component", type: BsCardHeaderComponent, selector: "bs-card-header", inputs: ["noPadding"] }, { kind: "component", type: BsFormComponent, selector: "bs-form", inputs: ["action", "method"], outputs: ["submitted"] }, { kind: "directive", type: BsFormControlDirective, selector: "bs-form input:not(.no-form-control), bs-form textarea:not(.no-form-control)" }, { kind: "component", type: BsSpinnerComponent, selector: "bs-spinner", inputs: ["type", "color"] }, { kind: "pipe", type: TranslateKeyPipe, name: "t" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
897
449
  }
898
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: SparkForgotPasswordComponent, decorators: [{
450
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SparkForgotPasswordComponent, decorators: [{
899
451
  type: Component,
900
- args: [{
901
- selector: 'spark-forgot-password',
902
- standalone: true,
903
- imports: [ReactiveFormsModule, RouterLink, BsFormModule, TranslateKeyPipe],
904
- template: `
905
- <div class="d-flex justify-content-center">
906
- <div class="card" style="width: 100%; max-width: 400px;">
907
- <div class="card-body">
908
- <h3 class="card-title text-center mb-4">{{ 'authForgotPasswordTitle' | t }}</h3>
909
-
910
- @if (successMessage()) {
911
- <div class="alert alert-success" role="alert">{{ successMessage() }}</div>
912
- }
913
-
914
- @if (errorMessage()) {
915
- <div class="alert alert-danger" role="alert">{{ errorMessage() }}</div>
916
- }
917
-
918
- @if (!successMessage()) {
919
- <p class="text-muted mb-3">
920
- {{ 'authForgotPasswordDescription' | t }}
921
- </p>
922
-
923
- <bs-form>
924
- <form [formGroup]="form" (ngSubmit)="onSubmit()">
925
- <div class="mb-3">
926
- <label for="email" class="form-label">{{ 'authEmail' | t }}</label>
927
- <input
928
- type="email"
929
- id="email"
930
- formControlName="email"
931
- autocomplete="email"
932
- />
933
- </div>
934
-
935
- <button
936
- type="submit"
937
- class="btn btn-primary w-100"
938
- [disabled]="loading()"
939
- >
940
- @if (loading()) {
941
- <span class="spinner-border spinner-border-sm me-1" role="status"></span>
942
- }
943
- {{ 'authSendResetLink' | t }}
944
- </button>
945
- </form>
946
- </bs-form>
947
- }
948
-
949
- <div class="mt-3 text-center">
950
- <a [routerLink]="routePaths.login">{{ 'authBackToLogin' | t }}</a>
951
- </div>
952
- </div>
953
- </div>
954
- </div>
955
- `,
956
- }]
452
+ args: [{ selector: 'spark-forgot-password', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [ReactiveFormsModule, RouterLink, BsAlertComponent, BsCardComponent, BsCardHeaderComponent, BsFormComponent, BsFormControlDirective, BsSpinnerComponent, TranslateKeyPipe], template: "<div class=\"d-flex justify-content-center\">\n <bs-card style=\"width: 100%; max-width: 400px;\">\n <bs-card-header>\n <h3 class=\"mb-0 text-center\">{{ 'authForgotPasswordTitle' | t }}</h3>\n </bs-card-header>\n <div class=\"p-4\">\n @if (successMessage()) {\n <bs-alert [type]=\"colors.success\" class=\"mb-3\">{{ successMessage() }}</bs-alert>\n }\n\n @if (errorMessage()) {\n <bs-alert [type]=\"colors.danger\" class=\"mb-3\">{{ errorMessage() }}</bs-alert>\n }\n\n @if (!successMessage()) {\n <p class=\"text-muted mb-3\">\n {{ 'authForgotPasswordDescription' | t }}\n </p>\n\n <bs-form>\n <form [formGroup]=\"form\" (ngSubmit)=\"onSubmit()\">\n <div class=\"mb-3\">\n <label for=\"email\" class=\"form-label\">{{ 'authEmail' | t }}</label>\n <input\n type=\"email\"\n id=\"email\"\n formControlName=\"email\"\n autocomplete=\"email\"\n />\n </div>\n\n <button\n type=\"submit\"\n class=\"btn btn-primary w-100\"\n [disabled]=\"loading()\"\n >\n @if (loading()) {\n <bs-spinner class=\"me-1\" />\n }\n {{ 'authSendResetLink' | t }}\n </button>\n </form>\n </bs-form>\n }\n\n <div class=\"mt-3 text-center\">\n <a [routerLink]=\"routePaths.login\">{{ 'authBackToLogin' | t }}</a>\n </div>\n </div>\n </bs-card>\n</div>\n" }]
957
453
  }] });
958
454
 
959
455
  var sparkForgotPassword_component = /*#__PURE__*/Object.freeze({
@@ -977,6 +473,7 @@ class SparkResetPasswordComponent {
977
473
  route = inject(ActivatedRoute);
978
474
  translation = inject(SparkAuthTranslationService);
979
475
  routePaths = inject(SPARK_AUTH_ROUTE_PATHS);
476
+ colors = Color;
980
477
  loading = signal(false, ...(ngDevMode ? [{ debugName: "loading" }] : []));
981
478
  errorMessage = signal('', ...(ngDevMode ? [{ debugName: "errorMessage" }] : []));
982
479
  successMessage = signal('', ...(ngDevMode ? [{ debugName: "successMessage" }] : []));
@@ -993,7 +490,7 @@ class SparkResetPasswordComponent {
993
490
  this.errorMessage.set(this.translation.t('authInvalidResetLink'));
994
491
  }
995
492
  }
996
- onSubmit() {
493
+ async onSubmit() {
997
494
  if (this.form.invalid) {
998
495
  this.form.markAllAsTouched();
999
496
  return;
@@ -1006,163 +503,28 @@ class SparkResetPasswordComponent {
1006
503
  this.errorMessage.set('');
1007
504
  this.successMessage.set('');
1008
505
  const { newPassword } = this.form.value;
1009
- this.authService.resetPassword(this.email, this.code, newPassword).subscribe({
1010
- next: () => {
1011
- this.loading.set(false);
1012
- this.successMessage.set(this.translation.t('authResetSuccess'));
1013
- },
1014
- error: (err) => {
1015
- this.loading.set(false);
1016
- if (err.error?.detail) {
1017
- this.errorMessage.set(err.error.detail);
1018
- }
1019
- else {
1020
- this.errorMessage.set(this.translation.t('authResetFailed'));
1021
- }
1022
- },
1023
- });
506
+ try {
507
+ await this.authService.resetPassword(this.email, this.code, newPassword);
508
+ this.successMessage.set(this.translation.t('authResetSuccess'));
509
+ }
510
+ catch (err) {
511
+ if (err instanceof HttpErrorResponse && err.error?.detail) {
512
+ this.errorMessage.set(err.error.detail);
513
+ }
514
+ else {
515
+ this.errorMessage.set(this.translation.t('authResetFailed'));
516
+ }
517
+ }
518
+ finally {
519
+ this.loading.set(false);
520
+ }
1024
521
  }
1025
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: SparkResetPasswordComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1026
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.5", type: SparkResetPasswordComponent, isStandalone: true, selector: "spark-reset-password", ngImport: i0, template: `
1027
- <div class="d-flex justify-content-center">
1028
- <div class="card" style="width: 100%; max-width: 400px;">
1029
- <div class="card-body">
1030
- <h3 class="card-title text-center mb-4">{{ 'authResetPassword' | t }}</h3>
1031
-
1032
- @if (successMessage()) {
1033
- <div class="alert alert-success" role="alert">
1034
- {{ successMessage() }}
1035
- <div class="mt-2">
1036
- <a [routerLink]="routePaths.login">{{ 'authGoToLogin' | t }}</a>
1037
- </div>
1038
- </div>
1039
- }
1040
-
1041
- @if (errorMessage()) {
1042
- <div class="alert alert-danger" role="alert">{{ errorMessage() }}</div>
1043
- }
1044
-
1045
- @if (!successMessage()) {
1046
- <bs-form>
1047
- <form [formGroup]="form" (ngSubmit)="onSubmit()">
1048
- <div class="mb-3">
1049
- <label for="newPassword" class="form-label">{{ 'authNewPassword' | t }}</label>
1050
- <input
1051
- type="password"
1052
- id="newPassword"
1053
- formControlName="newPassword"
1054
- autocomplete="new-password"
1055
- />
1056
- </div>
1057
-
1058
- <div class="mb-3">
1059
- <label for="confirmPassword" class="form-label">{{ 'authConfirmPassword' | t }}</label>
1060
- <input
1061
- type="password"
1062
- id="confirmPassword"
1063
- formControlName="confirmPassword"
1064
- autocomplete="new-password"
1065
- />
1066
- @if (form.touched && form.hasError('passwordMismatch')) {
1067
- <div class="text-danger mt-1">{{ 'authPasswordMismatch' | t }}</div>
1068
- }
1069
- </div>
1070
-
1071
- <button
1072
- type="submit"
1073
- class="btn btn-primary w-100"
1074
- [disabled]="loading()"
1075
- >
1076
- @if (loading()) {
1077
- <span class="spinner-border spinner-border-sm me-1" role="status"></span>
1078
- }
1079
- {{ 'authResetPassword' | t }}
1080
- </button>
1081
- </form>
1082
- </bs-form>
1083
-
1084
- <div class="mt-3 text-center">
1085
- <a [routerLink]="routePaths.login">{{ 'authBackToLogin' | t }}</a>
1086
- </div>
1087
- }
1088
- </div>
1089
- </div>
1090
- </div>
1091
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: BsFormModule }, { kind: "component", type: i2.BsFormComponent, selector: "bs-form", inputs: ["action", "method"], outputs: ["submitted"] }, { kind: "directive", type: i2.BsFormControlDirective, selector: "bs-form input:not(.no-form-control), bs-form textarea:not(.no-form-control)" }, { kind: "pipe", type: TranslateKeyPipe, name: "t" }] });
522
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SparkResetPasswordComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
523
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: SparkResetPasswordComponent, isStandalone: true, selector: "spark-reset-password", ngImport: i0, template: "<div class=\"d-flex justify-content-center\">\n <bs-card style=\"width: 100%; max-width: 400px;\">\n <bs-card-header>\n <h3 class=\"mb-0 text-center\">{{ 'authResetPassword' | t }}</h3>\n </bs-card-header>\n <div class=\"p-4\">\n @if (successMessage()) {\n <bs-alert [type]=\"colors.success\" class=\"mb-3\">\n {{ successMessage() }}\n <div class=\"mt-2\">\n <a [routerLink]=\"routePaths.login\">{{ 'authGoToLogin' | t }}</a>\n </div>\n </bs-alert>\n }\n\n @if (errorMessage()) {\n <bs-alert [type]=\"colors.danger\" class=\"mb-3\">{{ errorMessage() }}</bs-alert>\n }\n\n @if (!successMessage()) {\n <bs-form>\n <form [formGroup]=\"form\" (ngSubmit)=\"onSubmit()\">\n <div class=\"mb-3\">\n <label for=\"newPassword\" class=\"form-label\">{{ 'authNewPassword' | t }}</label>\n <input\n type=\"password\"\n id=\"newPassword\"\n formControlName=\"newPassword\"\n autocomplete=\"new-password\"\n />\n </div>\n\n <div class=\"mb-3\">\n <label for=\"confirmPassword\" class=\"form-label\">{{ 'authConfirmPassword' | t }}</label>\n <input\n type=\"password\"\n id=\"confirmPassword\"\n formControlName=\"confirmPassword\"\n autocomplete=\"new-password\"\n />\n @if (form.touched && form.hasError('passwordMismatch')) {\n <div class=\"text-danger mt-1\">{{ 'authPasswordMismatch' | t }}</div>\n }\n </div>\n\n <button\n type=\"submit\"\n class=\"btn btn-primary w-100\"\n [disabled]=\"loading()\"\n >\n @if (loading()) {\n <bs-spinner class=\"me-1\" />\n }\n {{ 'authResetPassword' | t }}\n </button>\n </form>\n </bs-form>\n\n <div class=\"mt-3 text-center\">\n <a [routerLink]=\"routePaths.login\">{{ 'authBackToLogin' | t }}</a>\n </div>\n }\n </div>\n </bs-card>\n</div>\n", dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: BsAlertComponent, selector: "bs-alert", inputs: ["type", "isVisible"], outputs: ["isVisibleChange", "afterOpenedOrClosed"] }, { kind: "component", type: BsCardComponent, selector: "bs-card", inputs: ["rounded"] }, { kind: "component", type: BsCardHeaderComponent, selector: "bs-card-header", inputs: ["noPadding"] }, { kind: "component", type: BsFormComponent, selector: "bs-form", inputs: ["action", "method"], outputs: ["submitted"] }, { kind: "directive", type: BsFormControlDirective, selector: "bs-form input:not(.no-form-control), bs-form textarea:not(.no-form-control)" }, { kind: "component", type: BsSpinnerComponent, selector: "bs-spinner", inputs: ["type", "color"] }, { kind: "pipe", type: TranslateKeyPipe, name: "t" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1092
524
  }
1093
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: SparkResetPasswordComponent, decorators: [{
525
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SparkResetPasswordComponent, decorators: [{
1094
526
  type: Component,
1095
- args: [{
1096
- selector: 'spark-reset-password',
1097
- standalone: true,
1098
- imports: [ReactiveFormsModule, RouterLink, BsFormModule, TranslateKeyPipe],
1099
- template: `
1100
- <div class="d-flex justify-content-center">
1101
- <div class="card" style="width: 100%; max-width: 400px;">
1102
- <div class="card-body">
1103
- <h3 class="card-title text-center mb-4">{{ 'authResetPassword' | t }}</h3>
1104
-
1105
- @if (successMessage()) {
1106
- <div class="alert alert-success" role="alert">
1107
- {{ successMessage() }}
1108
- <div class="mt-2">
1109
- <a [routerLink]="routePaths.login">{{ 'authGoToLogin' | t }}</a>
1110
- </div>
1111
- </div>
1112
- }
1113
-
1114
- @if (errorMessage()) {
1115
- <div class="alert alert-danger" role="alert">{{ errorMessage() }}</div>
1116
- }
1117
-
1118
- @if (!successMessage()) {
1119
- <bs-form>
1120
- <form [formGroup]="form" (ngSubmit)="onSubmit()">
1121
- <div class="mb-3">
1122
- <label for="newPassword" class="form-label">{{ 'authNewPassword' | t }}</label>
1123
- <input
1124
- type="password"
1125
- id="newPassword"
1126
- formControlName="newPassword"
1127
- autocomplete="new-password"
1128
- />
1129
- </div>
1130
-
1131
- <div class="mb-3">
1132
- <label for="confirmPassword" class="form-label">{{ 'authConfirmPassword' | t }}</label>
1133
- <input
1134
- type="password"
1135
- id="confirmPassword"
1136
- formControlName="confirmPassword"
1137
- autocomplete="new-password"
1138
- />
1139
- @if (form.touched && form.hasError('passwordMismatch')) {
1140
- <div class="text-danger mt-1">{{ 'authPasswordMismatch' | t }}</div>
1141
- }
1142
- </div>
1143
-
1144
- <button
1145
- type="submit"
1146
- class="btn btn-primary w-100"
1147
- [disabled]="loading()"
1148
- >
1149
- @if (loading()) {
1150
- <span class="spinner-border spinner-border-sm me-1" role="status"></span>
1151
- }
1152
- {{ 'authResetPassword' | t }}
1153
- </button>
1154
- </form>
1155
- </bs-form>
1156
-
1157
- <div class="mt-3 text-center">
1158
- <a [routerLink]="routePaths.login">{{ 'authBackToLogin' | t }}</a>
1159
- </div>
1160
- }
1161
- </div>
1162
- </div>
1163
- </div>
1164
- `,
1165
- }]
527
+ args: [{ selector: 'spark-reset-password', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [ReactiveFormsModule, RouterLink, BsAlertComponent, BsCardComponent, BsCardHeaderComponent, BsFormComponent, BsFormControlDirective, BsSpinnerComponent, TranslateKeyPipe], template: "<div class=\"d-flex justify-content-center\">\n <bs-card style=\"width: 100%; max-width: 400px;\">\n <bs-card-header>\n <h3 class=\"mb-0 text-center\">{{ 'authResetPassword' | t }}</h3>\n </bs-card-header>\n <div class=\"p-4\">\n @if (successMessage()) {\n <bs-alert [type]=\"colors.success\" class=\"mb-3\">\n {{ successMessage() }}\n <div class=\"mt-2\">\n <a [routerLink]=\"routePaths.login\">{{ 'authGoToLogin' | t }}</a>\n </div>\n </bs-alert>\n }\n\n @if (errorMessage()) {\n <bs-alert [type]=\"colors.danger\" class=\"mb-3\">{{ errorMessage() }}</bs-alert>\n }\n\n @if (!successMessage()) {\n <bs-form>\n <form [formGroup]=\"form\" (ngSubmit)=\"onSubmit()\">\n <div class=\"mb-3\">\n <label for=\"newPassword\" class=\"form-label\">{{ 'authNewPassword' | t }}</label>\n <input\n type=\"password\"\n id=\"newPassword\"\n formControlName=\"newPassword\"\n autocomplete=\"new-password\"\n />\n </div>\n\n <div class=\"mb-3\">\n <label for=\"confirmPassword\" class=\"form-label\">{{ 'authConfirmPassword' | t }}</label>\n <input\n type=\"password\"\n id=\"confirmPassword\"\n formControlName=\"confirmPassword\"\n autocomplete=\"new-password\"\n />\n @if (form.touched && form.hasError('passwordMismatch')) {\n <div class=\"text-danger mt-1\">{{ 'authPasswordMismatch' | t }}</div>\n }\n </div>\n\n <button\n type=\"submit\"\n class=\"btn btn-primary w-100\"\n [disabled]=\"loading()\"\n >\n @if (loading()) {\n <bs-spinner class=\"me-1\" />\n }\n {{ 'authResetPassword' | t }}\n </button>\n </form>\n </bs-form>\n\n <div class=\"mt-3 text-center\">\n <a [routerLink]=\"routePaths.login\">{{ 'authBackToLogin' | t }}</a>\n </div>\n }\n </div>\n </bs-card>\n</div>\n" }]
1166
528
  }] });
1167
529
 
1168
530
  var sparkResetPassword_component = /*#__PURE__*/Object.freeze({