@myrmidon/auth-jwt-admin 8.0.4 → 10.0.0

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,36 +1,35 @@
1
1
  import * as i0 from '@angular/core';
2
- import { inject, Injectable, input, output, computed, effect, ChangeDetectionStrategy, Component, signal } from '@angular/core';
3
- import * as i1 from '@angular/forms';
4
- import { FormBuilder, Validators, ReactiveFormsModule } from '@angular/forms';
2
+ import { inject, Injectable, input, output, computed, effect, ChangeDetectionStrategy, Component, signal, resource } from '@angular/core';
3
+ import { form, required, email, validateAsync, pattern, maxLength, validate, FormField, FormRoot, disabled } from '@angular/forms/signals';
4
+ import { BehaviorSubject, tap, firstValueFrom } from 'rxjs';
5
5
  import { MatSnackBar } from '@angular/material/snack-bar';
6
- import { retry, map, catchError, take } from 'rxjs/operators';
7
- import * as i3 from '@angular/material/form-field';
6
+ import * as i2 from '@angular/material/form-field';
8
7
  import { MatFormFieldModule } from '@angular/material/form-field';
9
- import * as i2 from '@angular/material/button';
8
+ import * as i1 from '@angular/material/button';
10
9
  import { MatButtonModule } from '@angular/material/button';
11
- import * as i4 from '@angular/material/icon';
10
+ import * as i3 from '@angular/material/icon';
12
11
  import { MatIconModule } from '@angular/material/icon';
13
- import * as i5 from '@angular/material/input';
12
+ import * as i4 from '@angular/material/input';
14
13
  import { MatInputModule } from '@angular/material/input';
15
- import * as i6 from '@angular/material/progress-spinner';
14
+ import * as i5 from '@angular/material/progress-spinner';
16
15
  import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
17
16
  import { HttpClient, HttpParams } from '@angular/common/http';
17
+ import { retry, map, catchError } from 'rxjs/operators';
18
18
  import { ErrorService, EnvService } from '@myrmidon/ngx-tools';
19
- import { BehaviorSubject, tap } from 'rxjs';
20
19
  import { PagedListStore } from '@myrmidon/paged-data-browsers';
21
- import * as i6$1 from '@angular/material/tooltip';
20
+ import * as i6 from '@angular/material/tooltip';
22
21
  import { MatTooltipModule } from '@angular/material/tooltip';
23
22
  import * as i4$1 from '@angular/material/paginator';
24
23
  import { MatPaginatorModule } from '@angular/material/paginator';
25
24
  import * as i7 from '@angular/common';
26
25
  import { CommonModule } from '@angular/common';
27
- import * as i2$1 from '@angular/material/expansion';
26
+ import * as i2$2 from '@angular/material/expansion';
28
27
  import { MatExpansionModule } from '@angular/material/expansion';
29
28
  import * as i5$1 from '@angular/material/progress-bar';
30
29
  import { MatProgressBarModule } from '@angular/material/progress-bar';
31
30
  import { AuthJwtService, GravatarService } from '@myrmidon/auth-jwt-login';
32
31
  import { DialogService } from '@myrmidon/ngx-mat-tools';
33
- import * as i3$1 from '@angular/material/checkbox';
32
+ import * as i2$1 from '@angular/material/checkbox';
34
33
  import { MatCheckboxModule } from '@angular/material/checkbox';
35
34
 
36
35
  /**
@@ -223,10 +222,10 @@ class AuthJwtAccountService {
223
222
  .delete(this._env.get('apiUrl') + 'accounts/' + name)
224
223
  .pipe(catchError(this._error.handleError));
225
224
  }
226
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: AuthJwtAccountService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
227
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: AuthJwtAccountService, providedIn: 'root' }); }
225
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: AuthJwtAccountService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
226
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: AuthJwtAccountService, providedIn: 'root' }); }
228
227
  }
229
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: AuthJwtAccountService, decorators: [{
228
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: AuthJwtAccountService, decorators: [{
230
229
  type: Injectable,
231
230
  args: [{
232
231
  providedIn: 'root',
@@ -234,55 +233,25 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImpor
234
233
  }] });
235
234
 
236
235
  /**
237
- * Password validators.
236
+ * Password validators as pure predicate functions returning Signal Forms
237
+ * compatible ValidationError objects ({ kind: string }).
238
238
  */
239
239
  class PasswordValidators {
240
- /** "Standard" password validator for my API services. */
241
240
  static standard() {
242
- return (control) => {
243
- if (!control.value) {
241
+ return (value) => {
242
+ if (!value)
244
243
  return null;
245
- }
246
- if (control.value.length < 8) {
247
- return {
248
- passwordtooshort: true,
249
- };
250
- }
251
- if (!/.*[A-Z].*/.test(control.value)) {
252
- return {
253
- noupperinpassword: true,
254
- };
255
- }
256
- if (!/.*[a-z].*/.test(control.value)) {
257
- return {
258
- nolowerinpassword: true,
259
- };
260
- }
261
- if (!/.*[A-Z].*/.test(control.value)) {
262
- return {
263
- noupperinpassword: true,
264
- };
265
- }
266
- if (!/.*[-`~!@#$%^&*()_+=\[\]{};:'",.<>/?|\\].*/.test(control.value)) {
267
- return {
268
- nosymbolinpassword: true,
269
- };
270
- }
244
+ if (value.length < 8)
245
+ return { kind: 'passwordtooshort' };
246
+ if (!/.*[A-Z].*/.test(value))
247
+ return { kind: 'noupperinpassword' };
248
+ if (!/.*[a-z].*/.test(value))
249
+ return { kind: 'nolowerinpassword' };
250
+ if (!/.*[-`~!@#$%^&*()_+=\[\]{};:'",.<>/?|\\].*/.test(value))
251
+ return { kind: 'nosymbolinpassword' };
271
252
  return null;
272
253
  };
273
254
  }
274
- static areEqual(controlName, otherControlName) {
275
- return (control) => {
276
- const g = control;
277
- const a = g.controls[controlName];
278
- const b = g.controls[otherControlName];
279
- return a.value !== b.value
280
- ? {
281
- areequal: true,
282
- }
283
- : null;
284
- };
285
- }
286
255
  }
287
256
 
288
257
  class UserListRepository {
@@ -398,10 +367,10 @@ class UserListRepository {
398
367
  });
399
368
  return promise;
400
369
  }
401
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: UserListRepository, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
402
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: UserListRepository, providedIn: 'root' }); }
370
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: UserListRepository, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
371
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: UserListRepository, providedIn: 'root' }); }
403
372
  }
404
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: UserListRepository, decorators: [{
373
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: UserListRepository, decorators: [{
405
374
  type: Injectable,
406
375
  args: [{ providedIn: 'root' }]
407
376
  }], ctorParameters: () => [] });
@@ -426,31 +395,53 @@ const DEFAULT_RULE_LABELS = {
426
395
  class PasswordChecklistComponent {
427
396
  constructor() {
428
397
  // Input signals for password and context
429
- this.password = input('', ...(ngDevMode ? [{ debugName: "password" }] : /* istanbul ignore next */ []));
430
- this.username = input('', ...(ngDevMode ? [{ debugName: "username" }] : /* istanbul ignore next */ []));
431
- this.email = input('', ...(ngDevMode ? [{ debugName: "email" }] : /* istanbul ignore next */ []));
432
- this.hidden = input(false, ...(ngDevMode ? [{ debugName: "hidden" }] : /* istanbul ignore next */ []));
398
+ this.password = input('', /* @ts-ignore */
399
+ ...(ngDevMode ? [{ debugName: "password" }] : /* istanbul ignore next */ []));
400
+ this.username = input('', /* @ts-ignore */
401
+ ...(ngDevMode ? [{ debugName: "username" }] : /* istanbul ignore next */ []));
402
+ this.email = input('', /* @ts-ignore */
403
+ ...(ngDevMode ? [{ debugName: "email" }] : /* istanbul ignore next */ []));
404
+ this.hidden = input(false, /* @ts-ignore */
405
+ ...(ngDevMode ? [{ debugName: "hidden" }] : /* istanbul ignore next */ []));
433
406
  // Input signals for rule configuration
434
- this.minLength = input(null, ...(ngDevMode ? [{ debugName: "minLength" }] : /* istanbul ignore next */ []));
435
- this.maxLength = input(null, ...(ngDevMode ? [{ debugName: "maxLength" }] : /* istanbul ignore next */ []));
436
- this.requireUppercase = input(false, ...(ngDevMode ? [{ debugName: "requireUppercase" }] : /* istanbul ignore next */ []));
437
- this.requireLowercase = input(false, ...(ngDevMode ? [{ debugName: "requireLowercase" }] : /* istanbul ignore next */ []));
438
- this.requireNumber = input(false, ...(ngDevMode ? [{ debugName: "requireNumber" }] : /* istanbul ignore next */ []));
439
- this.requireSymbol = input(false, ...(ngDevMode ? [{ debugName: "requireSymbol" }] : /* istanbul ignore next */ []));
440
- this.checkUsername = input(false, ...(ngDevMode ? [{ debugName: "checkUsername" }] : /* istanbul ignore next */ []));
441
- this.checkEmail = input(false, ...(ngDevMode ? [{ debugName: "checkEmail" }] : /* istanbul ignore next */ []));
407
+ this.minLength = input(null, /* @ts-ignore */
408
+ ...(ngDevMode ? [{ debugName: "minLength" }] : /* istanbul ignore next */ []));
409
+ this.maxLength = input(null, /* @ts-ignore */
410
+ ...(ngDevMode ? [{ debugName: "maxLength" }] : /* istanbul ignore next */ []));
411
+ this.requireUppercase = input(false, /* @ts-ignore */
412
+ ...(ngDevMode ? [{ debugName: "requireUppercase" }] : /* istanbul ignore next */ []));
413
+ this.requireLowercase = input(false, /* @ts-ignore */
414
+ ...(ngDevMode ? [{ debugName: "requireLowercase" }] : /* istanbul ignore next */ []));
415
+ this.requireNumber = input(false, /* @ts-ignore */
416
+ ...(ngDevMode ? [{ debugName: "requireNumber" }] : /* istanbul ignore next */ []));
417
+ this.requireSymbol = input(false, /* @ts-ignore */
418
+ ...(ngDevMode ? [{ debugName: "requireSymbol" }] : /* istanbul ignore next */ []));
419
+ this.checkUsername = input(false, /* @ts-ignore */
420
+ ...(ngDevMode ? [{ debugName: "checkUsername" }] : /* istanbul ignore next */ []));
421
+ this.checkEmail = input(false, /* @ts-ignore */
422
+ ...(ngDevMode ? [{ debugName: "checkEmail" }] : /* istanbul ignore next */ []));
442
423
  // Input signals for custom labels
443
- this.minLengthLabel = input(null, ...(ngDevMode ? [{ debugName: "minLengthLabel" }] : /* istanbul ignore next */ []));
444
- this.maxLengthLabel = input(null, ...(ngDevMode ? [{ debugName: "maxLengthLabel" }] : /* istanbul ignore next */ []));
445
- this.uppercaseLabel = input(null, ...(ngDevMode ? [{ debugName: "uppercaseLabel" }] : /* istanbul ignore next */ []));
446
- this.lowercaseLabel = input(null, ...(ngDevMode ? [{ debugName: "lowercaseLabel" }] : /* istanbul ignore next */ []));
447
- this.numberLabel = input(null, ...(ngDevMode ? [{ debugName: "numberLabel" }] : /* istanbul ignore next */ []));
448
- this.symbolLabel = input(null, ...(ngDevMode ? [{ debugName: "symbolLabel" }] : /* istanbul ignore next */ []));
449
- this.usernameLabel = input(null, ...(ngDevMode ? [{ debugName: "usernameLabel" }] : /* istanbul ignore next */ []));
450
- this.emailLabel = input(null, ...(ngDevMode ? [{ debugName: "emailLabel" }] : /* istanbul ignore next */ []));
424
+ this.minLengthLabel = input(null, /* @ts-ignore */
425
+ ...(ngDevMode ? [{ debugName: "minLengthLabel" }] : /* istanbul ignore next */ []));
426
+ this.maxLengthLabel = input(null, /* @ts-ignore */
427
+ ...(ngDevMode ? [{ debugName: "maxLengthLabel" }] : /* istanbul ignore next */ []));
428
+ this.uppercaseLabel = input(null, /* @ts-ignore */
429
+ ...(ngDevMode ? [{ debugName: "uppercaseLabel" }] : /* istanbul ignore next */ []));
430
+ this.lowercaseLabel = input(null, /* @ts-ignore */
431
+ ...(ngDevMode ? [{ debugName: "lowercaseLabel" }] : /* istanbul ignore next */ []));
432
+ this.numberLabel = input(null, /* @ts-ignore */
433
+ ...(ngDevMode ? [{ debugName: "numberLabel" }] : /* istanbul ignore next */ []));
434
+ this.symbolLabel = input(null, /* @ts-ignore */
435
+ ...(ngDevMode ? [{ debugName: "symbolLabel" }] : /* istanbul ignore next */ []));
436
+ this.usernameLabel = input(null, /* @ts-ignore */
437
+ ...(ngDevMode ? [{ debugName: "usernameLabel" }] : /* istanbul ignore next */ []));
438
+ this.emailLabel = input(null, /* @ts-ignore */
439
+ ...(ngDevMode ? [{ debugName: "emailLabel" }] : /* istanbul ignore next */ []));
451
440
  // Input signals for display options
452
- this.noChecklist = input(false, ...(ngDevMode ? [{ debugName: "noChecklist" }] : /* istanbul ignore next */ []));
453
- this.noMeter = input(false, ...(ngDevMode ? [{ debugName: "noMeter" }] : /* istanbul ignore next */ []));
441
+ this.noChecklist = input(false, /* @ts-ignore */
442
+ ...(ngDevMode ? [{ debugName: "noChecklist" }] : /* istanbul ignore next */ []));
443
+ this.noMeter = input(false, /* @ts-ignore */
444
+ ...(ngDevMode ? [{ debugName: "noMeter" }] : /* istanbul ignore next */ []));
454
445
  // Output signal for validity
455
446
  this.validChange = output();
456
447
  // Symbol regex pattern
@@ -459,33 +450,41 @@ class PasswordChecklistComponent {
459
450
  this.hasMinLength = computed(() => {
460
451
  const min = this.minLength();
461
452
  return min === null || this.password().length >= min;
462
- }, ...(ngDevMode ? [{ debugName: "hasMinLength" }] : /* istanbul ignore next */ []));
453
+ }, /* @ts-ignore */
454
+ ...(ngDevMode ? [{ debugName: "hasMinLength" }] : /* istanbul ignore next */ []));
463
455
  this.hasMaxLength = computed(() => {
464
456
  const max = this.maxLength();
465
457
  return max === null || this.password().length <= max;
466
- }, ...(ngDevMode ? [{ debugName: "hasMaxLength" }] : /* istanbul ignore next */ []));
458
+ }, /* @ts-ignore */
459
+ ...(ngDevMode ? [{ debugName: "hasMaxLength" }] : /* istanbul ignore next */ []));
467
460
  this.hasUppercase = computed(() => {
468
461
  return /[A-Z]/.test(this.password());
469
- }, ...(ngDevMode ? [{ debugName: "hasUppercase" }] : /* istanbul ignore next */ []));
462
+ }, /* @ts-ignore */
463
+ ...(ngDevMode ? [{ debugName: "hasUppercase" }] : /* istanbul ignore next */ []));
470
464
  this.hasLowercase = computed(() => {
471
465
  return /[a-z]/.test(this.password());
472
- }, ...(ngDevMode ? [{ debugName: "hasLowercase" }] : /* istanbul ignore next */ []));
466
+ }, /* @ts-ignore */
467
+ ...(ngDevMode ? [{ debugName: "hasLowercase" }] : /* istanbul ignore next */ []));
473
468
  this.hasNumber = computed(() => {
474
469
  return /[0-9]/.test(this.password());
475
- }, ...(ngDevMode ? [{ debugName: "hasNumber" }] : /* istanbul ignore next */ []));
470
+ }, /* @ts-ignore */
471
+ ...(ngDevMode ? [{ debugName: "hasNumber" }] : /* istanbul ignore next */ []));
476
472
  this.hasSymbol = computed(() => {
477
473
  return this.symbolRegex.test(this.password());
478
- }, ...(ngDevMode ? [{ debugName: "hasSymbol" }] : /* istanbul ignore next */ []));
474
+ }, /* @ts-ignore */
475
+ ...(ngDevMode ? [{ debugName: "hasSymbol" }] : /* istanbul ignore next */ []));
479
476
  this.noUsernameInPassword = computed(() => {
480
477
  const username = this.username().toLowerCase();
481
478
  const pwd = this.password().toLowerCase();
482
479
  return !username || !pwd || !pwd.includes(username);
483
- }, ...(ngDevMode ? [{ debugName: "noUsernameInPassword" }] : /* istanbul ignore next */ []));
480
+ }, /* @ts-ignore */
481
+ ...(ngDevMode ? [{ debugName: "noUsernameInPassword" }] : /* istanbul ignore next */ []));
484
482
  this.noEmailInPassword = computed(() => {
485
483
  const email = this.email().toLowerCase();
486
484
  const pwd = this.password().toLowerCase();
487
485
  return !email || !pwd || !pwd.includes(email);
488
- }, ...(ngDevMode ? [{ debugName: "noEmailInPassword" }] : /* istanbul ignore next */ []));
486
+ }, /* @ts-ignore */
487
+ ...(ngDevMode ? [{ debugName: "noEmailInPassword" }] : /* istanbul ignore next */ []));
489
488
  // Computed signal for all rules with their status
490
489
  this.rules = computed(() => {
491
490
  const rules = [];
@@ -546,7 +545,8 @@ class PasswordChecklistComponent {
546
545
  });
547
546
  }
548
547
  return rules;
549
- }, ...(ngDevMode ? [{ debugName: "rules" }] : /* istanbul ignore next */ []));
548
+ }, /* @ts-ignore */
549
+ ...(ngDevMode ? [{ debugName: "rules" }] : /* istanbul ignore next */ []));
550
550
  // Computed signal to check if a specific rule is satisfied
551
551
  this.isRuleSatisfied = computed(() => {
552
552
  return (ruleId) => {
@@ -571,7 +571,8 @@ class PasswordChecklistComponent {
571
571
  return false;
572
572
  }
573
573
  };
574
- }, ...(ngDevMode ? [{ debugName: "isRuleSatisfied" }] : /* istanbul ignore next */ []));
574
+ }, /* @ts-ignore */
575
+ ...(ngDevMode ? [{ debugName: "isRuleSatisfied" }] : /* istanbul ignore next */ []));
575
576
  // Computed signal for overall validity
576
577
  this.isValid = computed(() => {
577
578
  const rules = this.rules();
@@ -580,7 +581,8 @@ class PasswordChecklistComponent {
580
581
  }
581
582
  const checkFn = this.isRuleSatisfied();
582
583
  return rules.every((rule) => checkFn(rule.id));
583
- }, ...(ngDevMode ? [{ debugName: "isValid" }] : /* istanbul ignore next */ []));
584
+ }, /* @ts-ignore */
585
+ ...(ngDevMode ? [{ debugName: "isValid" }] : /* istanbul ignore next */ []));
584
586
  // Computed signal for password strength (0-100)
585
587
  this.strength = computed(() => {
586
588
  const pwd = this.password();
@@ -619,7 +621,8 @@ class PasswordChecklistComponent {
619
621
  score = Math.min(score, 70);
620
622
  }
621
623
  return Math.min(Math.round(score), 100);
622
- }, ...(ngDevMode ? [{ debugName: "strength" }] : /* istanbul ignore next */ []));
624
+ }, /* @ts-ignore */
625
+ ...(ngDevMode ? [{ debugName: "strength" }] : /* istanbul ignore next */ []));
623
626
  // Computed signal for strength level (0-4)
624
627
  this.strengthLevel = computed(() => {
625
628
  const strength = this.strength();
@@ -632,16 +635,19 @@ class PasswordChecklistComponent {
632
635
  if (strength <= 80)
633
636
  return 3;
634
637
  return 4;
635
- }, ...(ngDevMode ? [{ debugName: "strengthLevel" }] : /* istanbul ignore next */ []));
638
+ }, /* @ts-ignore */
639
+ ...(ngDevMode ? [{ debugName: "strengthLevel" }] : /* istanbul ignore next */ []));
636
640
  // Computed signal for strength color
637
641
  this.strengthColor = computed(() => {
638
642
  const colors = ['#F00', '#F90', '#FF0', '#9F0', '#0F0'];
639
643
  return colors[this.strengthLevel()];
640
- }, ...(ngDevMode ? [{ debugName: "strengthColor" }] : /* istanbul ignore next */ []));
644
+ }, /* @ts-ignore */
645
+ ...(ngDevMode ? [{ debugName: "strengthColor" }] : /* istanbul ignore next */ []));
641
646
  // Computed signal for strength percentage
642
647
  this.strengthPercent = computed(() => {
643
648
  return this.strength();
644
- }, ...(ngDevMode ? [{ debugName: "strengthPercent" }] : /* istanbul ignore next */ []));
649
+ }, /* @ts-ignore */
650
+ ...(ngDevMode ? [{ debugName: "strengthPercent" }] : /* istanbul ignore next */ []));
645
651
  // Effect to emit validity changes
646
652
  // This will automatically track the isValid computed signal
647
653
  effect(() => {
@@ -675,10 +681,10 @@ class PasswordChecklistComponent {
675
681
  checkRule(ruleId) {
676
682
  return this.isRuleSatisfied()(ruleId);
677
683
  }
678
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PasswordChecklistComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
679
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.4", type: PasswordChecklistComponent, isStandalone: true, selector: "auth-jwt-password-checklist", inputs: { password: { classPropertyName: "password", publicName: "password", isSignal: true, isRequired: false, transformFunction: null }, username: { classPropertyName: "username", publicName: "username", isSignal: true, isRequired: false, transformFunction: null }, email: { classPropertyName: "email", publicName: "email", isSignal: true, isRequired: false, transformFunction: null }, hidden: { classPropertyName: "hidden", publicName: "hidden", isSignal: true, isRequired: false, transformFunction: null }, minLength: { classPropertyName: "minLength", publicName: "minLength", isSignal: true, isRequired: false, transformFunction: null }, maxLength: { classPropertyName: "maxLength", publicName: "maxLength", isSignal: true, isRequired: false, transformFunction: null }, requireUppercase: { classPropertyName: "requireUppercase", publicName: "requireUppercase", isSignal: true, isRequired: false, transformFunction: null }, requireLowercase: { classPropertyName: "requireLowercase", publicName: "requireLowercase", isSignal: true, isRequired: false, transformFunction: null }, requireNumber: { classPropertyName: "requireNumber", publicName: "requireNumber", isSignal: true, isRequired: false, transformFunction: null }, requireSymbol: { classPropertyName: "requireSymbol", publicName: "requireSymbol", isSignal: true, isRequired: false, transformFunction: null }, checkUsername: { classPropertyName: "checkUsername", publicName: "checkUsername", isSignal: true, isRequired: false, transformFunction: null }, checkEmail: { classPropertyName: "checkEmail", publicName: "checkEmail", isSignal: true, isRequired: false, transformFunction: null }, minLengthLabel: { classPropertyName: "minLengthLabel", publicName: "minLengthLabel", isSignal: true, isRequired: false, transformFunction: null }, maxLengthLabel: { classPropertyName: "maxLengthLabel", publicName: "maxLengthLabel", isSignal: true, isRequired: false, transformFunction: null }, uppercaseLabel: { classPropertyName: "uppercaseLabel", publicName: "uppercaseLabel", isSignal: true, isRequired: false, transformFunction: null }, lowercaseLabel: { classPropertyName: "lowercaseLabel", publicName: "lowercaseLabel", isSignal: true, isRequired: false, transformFunction: null }, numberLabel: { classPropertyName: "numberLabel", publicName: "numberLabel", isSignal: true, isRequired: false, transformFunction: null }, symbolLabel: { classPropertyName: "symbolLabel", publicName: "symbolLabel", isSignal: true, isRequired: false, transformFunction: null }, usernameLabel: { classPropertyName: "usernameLabel", publicName: "usernameLabel", isSignal: true, isRequired: false, transformFunction: null }, emailLabel: { classPropertyName: "emailLabel", publicName: "emailLabel", isSignal: true, isRequired: false, transformFunction: null }, noChecklist: { classPropertyName: "noChecklist", publicName: "noChecklist", isSignal: true, isRequired: false, transformFunction: null }, noMeter: { classPropertyName: "noMeter", publicName: "noMeter", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { validChange: "validChange" }, ngImport: i0, template: "@if (!hidden()) {\r\n<div class=\"password-checklist-container\">\r\n <!-- Strength Meter -->\r\n @if (!noMeter() && password()) {\r\n <div class=\"strength-meter\">\r\n <div class=\"strength-label\">\r\n <small>Strength:</small>\r\n </div>\r\n <div class=\"strength-bar-container\">\r\n <div\r\n class=\"strength-bar\"\r\n [style.width.%]=\"strengthPercent()\"\r\n [style.background-color]=\"strengthColor()\"\r\n ></div>\r\n </div>\r\n <div class=\"strength-percentage\">\r\n <small>{{ strengthPercent() }}%</small>\r\n </div>\r\n </div>\r\n }\r\n\r\n <!-- Checklist -->\r\n @if (!noChecklist() && password() && rules().length > 0) {\r\n <div class=\"checklist\">\r\n <ul class=\"checklist-items\">\r\n @for (rule of rules(); track rule.id) {\r\n <li\r\n class=\"checklist-item\"\r\n [class.satisfied]=\"checkRule(rule.id)\"\r\n [class.unsatisfied]=\"!checkRule(rule.id)\"\r\n >\r\n <span class=\"checklist-icon\">\r\n {{ checkRule(rule.id) ? '\u2713' : '\u2717' }}\r\n </span>\r\n <span class=\"checklist-text\">{{ rule.label }}</span>\r\n </li>\r\n }\r\n </ul>\r\n </div>\r\n }\r\n</div>\r\n}\r\n", styles: [".password-checklist-container{margin-top:.5rem}.strength-meter{display:flex;align-items:center;gap:.5rem;margin-bottom:.75rem}.strength-label{flex-shrink:0}.strength-bar-container{position:relative;flex:1;height:6px;background-color:var(--mat-sys-surface-variant);border-radius:3px;overflow:hidden}.strength-bar{height:100%;transition:width .3s ease,background-color .3s ease;border-radius:3px}.strength-percentage{flex-shrink:0;min-width:3rem;text-align:right}.checklist{font-size:.875rem}.checklist-items{list-style:none;margin:0;padding:0}.checklist-item{display:flex;align-items:center;gap:.5rem;padding:.25rem 0;transition:color .2s ease}.checklist-icon{display:inline-flex;align-items:center;justify-content:center;width:1.25rem;height:1.25rem;border-radius:50%;font-weight:700;font-size:.75rem;flex-shrink:0;transition:background-color .2s ease,color .2s ease}.checklist-item.satisfied .checklist-icon{background-color:var(--mat-sys-success);color:var(--mat-sys-on-success)}.checklist-item.unsatisfied .checklist-icon{background-color:var(--mat-sys-error);color:var(--mat-sys-on-error)}.checklist-text{flex:1}.checklist-item.satisfied .checklist-text{color:var(--mat-sys-success)}.checklist-item.unsatisfied .checklist-text{color:var(--mat-sys-error)}@media(prefers-color-scheme:dark){.strength-bar-container{background-color:var(--mat-sys-surface-variant)}.checklist-item.satisfied .checklist-text{color:var(--mat-sys-success)}.checklist-item.unsatisfied .checklist-text{color:var(--mat-sys-error)}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
684
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: PasswordChecklistComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
685
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.4", type: PasswordChecklistComponent, isStandalone: true, selector: "auth-jwt-password-checklist", inputs: { password: { classPropertyName: "password", publicName: "password", isSignal: true, isRequired: false, transformFunction: null }, username: { classPropertyName: "username", publicName: "username", isSignal: true, isRequired: false, transformFunction: null }, email: { classPropertyName: "email", publicName: "email", isSignal: true, isRequired: false, transformFunction: null }, hidden: { classPropertyName: "hidden", publicName: "hidden", isSignal: true, isRequired: false, transformFunction: null }, minLength: { classPropertyName: "minLength", publicName: "minLength", isSignal: true, isRequired: false, transformFunction: null }, maxLength: { classPropertyName: "maxLength", publicName: "maxLength", isSignal: true, isRequired: false, transformFunction: null }, requireUppercase: { classPropertyName: "requireUppercase", publicName: "requireUppercase", isSignal: true, isRequired: false, transformFunction: null }, requireLowercase: { classPropertyName: "requireLowercase", publicName: "requireLowercase", isSignal: true, isRequired: false, transformFunction: null }, requireNumber: { classPropertyName: "requireNumber", publicName: "requireNumber", isSignal: true, isRequired: false, transformFunction: null }, requireSymbol: { classPropertyName: "requireSymbol", publicName: "requireSymbol", isSignal: true, isRequired: false, transformFunction: null }, checkUsername: { classPropertyName: "checkUsername", publicName: "checkUsername", isSignal: true, isRequired: false, transformFunction: null }, checkEmail: { classPropertyName: "checkEmail", publicName: "checkEmail", isSignal: true, isRequired: false, transformFunction: null }, minLengthLabel: { classPropertyName: "minLengthLabel", publicName: "minLengthLabel", isSignal: true, isRequired: false, transformFunction: null }, maxLengthLabel: { classPropertyName: "maxLengthLabel", publicName: "maxLengthLabel", isSignal: true, isRequired: false, transformFunction: null }, uppercaseLabel: { classPropertyName: "uppercaseLabel", publicName: "uppercaseLabel", isSignal: true, isRequired: false, transformFunction: null }, lowercaseLabel: { classPropertyName: "lowercaseLabel", publicName: "lowercaseLabel", isSignal: true, isRequired: false, transformFunction: null }, numberLabel: { classPropertyName: "numberLabel", publicName: "numberLabel", isSignal: true, isRequired: false, transformFunction: null }, symbolLabel: { classPropertyName: "symbolLabel", publicName: "symbolLabel", isSignal: true, isRequired: false, transformFunction: null }, usernameLabel: { classPropertyName: "usernameLabel", publicName: "usernameLabel", isSignal: true, isRequired: false, transformFunction: null }, emailLabel: { classPropertyName: "emailLabel", publicName: "emailLabel", isSignal: true, isRequired: false, transformFunction: null }, noChecklist: { classPropertyName: "noChecklist", publicName: "noChecklist", isSignal: true, isRequired: false, transformFunction: null }, noMeter: { classPropertyName: "noMeter", publicName: "noMeter", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { validChange: "validChange" }, ngImport: i0, template: "@if (!hidden()) {\r\n<div class=\"password-checklist-container\">\r\n <!-- Strength Meter -->\r\n @if (!noMeter() && password()) {\r\n <div class=\"strength-meter\">\r\n <div class=\"strength-label\">\r\n <small>Strength:</small>\r\n </div>\r\n <div class=\"strength-bar-container\">\r\n <div\r\n class=\"strength-bar\"\r\n [style.width.%]=\"strengthPercent()\"\r\n [style.background-color]=\"strengthColor()\"\r\n ></div>\r\n </div>\r\n <div class=\"strength-percentage\">\r\n <small>{{ strengthPercent() }}%</small>\r\n </div>\r\n </div>\r\n }\r\n\r\n <!-- Checklist -->\r\n @if (!noChecklist() && password() && rules().length > 0) {\r\n <div class=\"checklist\">\r\n <ul class=\"checklist-items\">\r\n @for (rule of rules(); track rule.id) {\r\n <li\r\n class=\"checklist-item\"\r\n [class.satisfied]=\"checkRule(rule.id)\"\r\n [class.unsatisfied]=\"!checkRule(rule.id)\"\r\n >\r\n <span class=\"checklist-icon\">\r\n {{ checkRule(rule.id) ? '\u2713' : '\u2717' }}\r\n </span>\r\n <span class=\"checklist-text\">{{ rule.label }}</span>\r\n </li>\r\n }\r\n </ul>\r\n </div>\r\n }\r\n</div>\r\n}\r\n", styles: [".password-checklist-container{margin-top:.5rem}.strength-meter{display:flex;align-items:center;gap:.5rem;margin-bottom:.75rem}.strength-label{flex-shrink:0}.strength-bar-container{position:relative;flex:1;height:6px;background-color:var(--mat-sys-surface-variant);border-radius:3px;overflow:hidden}.strength-bar{height:100%;transition:width .3s ease,background-color .3s ease;border-radius:3px}.strength-percentage{flex-shrink:0;min-width:3rem;text-align:right}.checklist{font-size:.875rem}.checklist-items{list-style:none;margin:0;padding:0}.checklist-item{display:flex;align-items:center;gap:.5rem;padding:.25rem 0;transition:color .2s ease}.checklist-icon{display:inline-flex;align-items:center;justify-content:center;width:1.25rem;height:1.25rem;border-radius:50%;font-weight:700;font-size:.75rem;flex-shrink:0;transition:background-color .2s ease,color .2s ease}.checklist-item.satisfied .checklist-icon{background-color:var(--mat-sys-success);color:var(--mat-sys-on-success)}.checklist-item.unsatisfied .checklist-icon{background-color:var(--mat-sys-error);color:var(--mat-sys-on-error)}.checklist-text{flex:1}.checklist-item.satisfied .checklist-text{color:var(--mat-sys-success)}.checklist-item.unsatisfied .checklist-text{color:var(--mat-sys-error)}@media(prefers-color-scheme:dark){.strength-bar-container{background-color:var(--mat-sys-surface-variant)}.checklist-item.satisfied .checklist-text{color:var(--mat-sys-success)}.checklist-item.unsatisfied .checklist-text{color:var(--mat-sys-error)}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
680
686
  }
681
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PasswordChecklistComponent, decorators: [{
687
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: PasswordChecklistComponent, decorators: [{
682
688
  type: Component,
683
689
  args: [{ selector: 'auth-jwt-password-checklist', standalone: true, imports: [], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (!hidden()) {\r\n<div class=\"password-checklist-container\">\r\n <!-- Strength Meter -->\r\n @if (!noMeter() && password()) {\r\n <div class=\"strength-meter\">\r\n <div class=\"strength-label\">\r\n <small>Strength:</small>\r\n </div>\r\n <div class=\"strength-bar-container\">\r\n <div\r\n class=\"strength-bar\"\r\n [style.width.%]=\"strengthPercent()\"\r\n [style.background-color]=\"strengthColor()\"\r\n ></div>\r\n </div>\r\n <div class=\"strength-percentage\">\r\n <small>{{ strengthPercent() }}%</small>\r\n </div>\r\n </div>\r\n }\r\n\r\n <!-- Checklist -->\r\n @if (!noChecklist() && password() && rules().length > 0) {\r\n <div class=\"checklist\">\r\n <ul class=\"checklist-items\">\r\n @for (rule of rules(); track rule.id) {\r\n <li\r\n class=\"checklist-item\"\r\n [class.satisfied]=\"checkRule(rule.id)\"\r\n [class.unsatisfied]=\"!checkRule(rule.id)\"\r\n >\r\n <span class=\"checklist-icon\">\r\n {{ checkRule(rule.id) ? '\u2713' : '\u2717' }}\r\n </span>\r\n <span class=\"checklist-text\">{{ rule.label }}</span>\r\n </li>\r\n }\r\n </ul>\r\n </div>\r\n }\r\n</div>\r\n}\r\n", styles: [".password-checklist-container{margin-top:.5rem}.strength-meter{display:flex;align-items:center;gap:.5rem;margin-bottom:.75rem}.strength-label{flex-shrink:0}.strength-bar-container{position:relative;flex:1;height:6px;background-color:var(--mat-sys-surface-variant);border-radius:3px;overflow:hidden}.strength-bar{height:100%;transition:width .3s ease,background-color .3s ease;border-radius:3px}.strength-percentage{flex-shrink:0;min-width:3rem;text-align:right}.checklist{font-size:.875rem}.checklist-items{list-style:none;margin:0;padding:0}.checklist-item{display:flex;align-items:center;gap:.5rem;padding:.25rem 0;transition:color .2s ease}.checklist-icon{display:inline-flex;align-items:center;justify-content:center;width:1.25rem;height:1.25rem;border-radius:50%;font-weight:700;font-size:.75rem;flex-shrink:0;transition:background-color .2s ease,color .2s ease}.checklist-item.satisfied .checklist-icon{background-color:var(--mat-sys-success);color:var(--mat-sys-on-success)}.checklist-item.unsatisfied .checklist-icon{background-color:var(--mat-sys-error);color:var(--mat-sys-on-error)}.checklist-text{flex:1}.checklist-item.satisfied .checklist-text{color:var(--mat-sys-success)}.checklist-item.unsatisfied .checklist-text{color:var(--mat-sys-error)}@media(prefers-color-scheme:dark){.strength-bar-container{background-color:var(--mat-sys-surface-variant)}.checklist-item.satisfied .checklist-text{color:var(--mat-sys-success)}.checklist-item.unsatisfied .checklist-text{color:var(--mat-sys-error)}}\n"] }]
684
690
  }], ctorParameters: () => [], propDecorators: { password: [{ type: i0.Input, args: [{ isSignal: true, alias: "password", required: false }] }], username: [{ type: i0.Input, args: [{ isSignal: true, alias: "username", required: false }] }], email: [{ type: i0.Input, args: [{ isSignal: true, alias: "email", required: false }] }], hidden: [{ type: i0.Input, args: [{ isSignal: true, alias: "hidden", required: false }] }], minLength: [{ type: i0.Input, args: [{ isSignal: true, alias: "minLength", required: false }] }], maxLength: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxLength", required: false }] }], requireUppercase: [{ type: i0.Input, args: [{ isSignal: true, alias: "requireUppercase", required: false }] }], requireLowercase: [{ type: i0.Input, args: [{ isSignal: true, alias: "requireLowercase", required: false }] }], requireNumber: [{ type: i0.Input, args: [{ isSignal: true, alias: "requireNumber", required: false }] }], requireSymbol: [{ type: i0.Input, args: [{ isSignal: true, alias: "requireSymbol", required: false }] }], checkUsername: [{ type: i0.Input, args: [{ isSignal: true, alias: "checkUsername", required: false }] }], checkEmail: [{ type: i0.Input, args: [{ isSignal: true, alias: "checkEmail", required: false }] }], minLengthLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "minLengthLabel", required: false }] }], maxLengthLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxLengthLabel", required: false }] }], uppercaseLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "uppercaseLabel", required: false }] }], lowercaseLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "lowercaseLabel", required: false }] }], numberLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "numberLabel", required: false }] }], symbolLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "symbolLabel", required: false }] }], usernameLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "usernameLabel", required: false }] }], emailLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "emailLabel", required: false }] }], noChecklist: [{ type: i0.Input, args: [{ isSignal: true, alias: "noChecklist", required: false }] }], noMeter: [{ type: i0.Input, args: [{ isSignal: true, alias: "noMeter", required: false }] }], validChange: [{ type: i0.Output, args: ["validChange"] }] } });
@@ -691,176 +697,124 @@ class AuthJwtRegistrationComponent {
691
697
  this._snackbar = inject(MatSnackBar);
692
698
  this._accountService = inject(AuthJwtAccountService);
693
699
  this._userRepository = inject(UserListRepository);
694
- /**
695
- * True if the registration should be confirmed automatically.
696
- */
697
- this.autoConfirm = input(...(ngDevMode ? [undefined, { debugName: "autoConfirm" }] : /* istanbul ignore next */ []));
698
- /**
699
- * Emitted when a user was successfully registered. Usually
700
- * you should handle this to move away from the registration
701
- * page.
702
- */
703
- this.registered = output();
704
- this.busy = signal(undefined, ...(ngDevMode ? [{ debugName: "busy" }] : /* istanbul ignore next */ []));
705
- this.hidePassword = signal(true, ...(ngDevMode ? [{ debugName: "hidePassword" }] : /* istanbul ignore next */ []));
706
- const formBuilder = inject(FormBuilder);
707
- // form
708
- this.email = formBuilder.control('', {
709
- validators: [Validators.required, Validators.email],
710
- asyncValidators: this.getUniqueEmailValidator(this._accountService).bind(this),
711
- nonNullable: true,
712
- });
713
- this.name = formBuilder.control('', {
714
- validators: Validators.required,
715
- asyncValidators: this.getUniqueNameValidator(this._accountService).bind(this),
716
- nonNullable: true,
717
- });
718
- this.firstName = formBuilder.control('', {
719
- validators: [Validators.required, Validators.maxLength(50)],
720
- nonNullable: true,
721
- });
722
- this.lastName = formBuilder.control('', {
723
- validators: [Validators.required, Validators.maxLength(50)],
724
- nonNullable: true,
725
- });
726
- // http://stackoverflow.com/questions/35474991/angular-2-form-validating-for-repeat-password
727
- this.password = formBuilder.control('', {
728
- validators: [Validators.required, PasswordValidators.standard()],
729
- nonNullable: true,
730
- });
731
- this.confirmPassword = formBuilder.control('', {
732
- validators: Validators.required,
733
- nonNullable: true,
734
- });
735
- this.form = formBuilder.group({
736
- email: this.email,
737
- name: this.name,
738
- firstName: this.firstName,
739
- lastName: this.lastName,
740
- password: this.password,
741
- confirmPassword: this.confirmPassword,
742
- }, {
743
- validators: [
744
- PasswordValidators.areEqual('password', 'confirmPassword'),
745
- ],
746
- });
747
- }
748
- /**
749
- * Creates a unique name validator. There is no dependency injection at this level,
750
- * but we can use closures. As a matter of fact, we have access to the service instance
751
- * from the component where we register validators. So we will implement a function
752
- * that will accept the service as parameter, and create the actual validation function.
753
- * This function will have access to the service when called during the validation process.
754
- * See http://restlet.com/blog/2016/02/17/implementing-angular2-forms-beyond-basics-part-2/.
755
- */
756
- getUniqueNameValidator(service) {
757
- return (control) => {
758
- return new Promise((resolve, reject) => {
759
- // avoid checking if empty
760
- if (!control.value) {
761
- resolve(null);
762
- }
763
- else {
764
- service.isNameRegistered(control.value).subscribe({
765
- next: (data) => {
766
- if (!data.isExisting) {
767
- resolve(null);
768
- }
769
- else {
770
- resolve({ uniqueName: true });
771
- }
772
- },
773
- error: (error) => {
774
- console.error('Unique name validator error' +
775
- (error ? JSON.stringify(error) : ''));
776
- resolve({ uniqueName: true });
777
- },
778
- });
779
- }
700
+ this.formModel = signal({
701
+ email: '',
702
+ name: '',
703
+ firstName: '',
704
+ lastName: '',
705
+ password: '',
706
+ confirmPassword: '',
707
+ }, /* @ts-ignore */
708
+ ...(ngDevMode ? [{ debugName: "formModel" }] : /* istanbul ignore next */ []));
709
+ this.userForm = form(this.formModel, (p) => {
710
+ // email
711
+ required(p.email);
712
+ email(p.email);
713
+ validateAsync(p.email, {
714
+ params: ({ value }) => value() || undefined,
715
+ factory: (emailParam) => resource({
716
+ params: () => emailParam(),
717
+ loader: async ({ params: emailVal }) => firstValueFrom(this._accountService.isEmailRegistered(emailVal)),
718
+ }),
719
+ onSuccess: (result) => result?.isExisting ? { kind: 'uniqueEmail' } : undefined,
720
+ onError: (err) => {
721
+ console.error('Unique email validator error:', err);
722
+ return { kind: 'uniqueEmail' };
723
+ },
724
+ debounce: 300,
780
725
  });
781
- };
782
- }
783
- getUniqueEmailValidator(service) {
784
- return (control) => {
785
- return new Promise((resolve, reject) => {
786
- // avoid checking if empty
787
- if (!control.value) {
788
- resolve(null);
789
- }
790
- else {
791
- service.isEmailRegistered(control.value).subscribe({
792
- next: (data) => {
793
- if (!data.isExisting) {
794
- resolve(null);
795
- }
796
- else {
797
- resolve({ uniqueEmail: true });
798
- }
799
- },
800
- error: (error) => {
801
- console.error('Unique email validator error' +
802
- (error ? JSON.stringify(error) : ''));
803
- resolve({ uniqueEmail: true });
804
- },
805
- });
806
- }
726
+ // name
727
+ required(p.name);
728
+ pattern(p.name, /^[a-zA-Z][a-zA-Z0-9]{2,49}$/);
729
+ validateAsync(p.name, {
730
+ params: ({ value }) => value() || undefined,
731
+ factory: (nameParam) => resource({
732
+ params: () => nameParam(),
733
+ loader: async ({ params: nameVal }) => firstValueFrom(this._accountService.isNameRegistered(nameVal)),
734
+ }),
735
+ onSuccess: (result) => result?.isExisting ? { kind: 'uniqueName' } : undefined,
736
+ onError: (err) => {
737
+ console.error('Unique name validator error:', err);
738
+ return { kind: 'uniqueName' };
739
+ },
740
+ debounce: 300,
807
741
  });
808
- };
809
- }
810
- onSubmit() {
811
- if (!this.form.valid ||
812
- this.busy() ||
813
- this.name.pending ||
814
- this.email.pending) {
815
- return;
816
- }
817
- const model = {
818
- email: this.email.value,
819
- name: this.name.value,
820
- firstName: this.firstName.value,
821
- lastName: this.lastName.value,
822
- password: this.password.value,
823
- };
824
- this.busy.set(true);
825
- this._accountService
826
- .register(model, this.autoConfirm())
827
- .pipe(take(1))
828
- .subscribe({
829
- next: () => {
830
- this.busy.set(false);
831
- this._snackbar.open($localize `:auth-jwt-admin:Registration succeeded`, 'OK');
832
- this._userRepository.reset();
833
- this.registered.emit();
834
- },
835
- error: (error) => {
836
- this.busy.set(false);
837
- console.error('Registration error' + (error ? JSON.stringify(error) : ''));
838
- this._snackbar.open($localize `:auth-jwt-admin:Registration error`, 'OK');
742
+ // first/last name
743
+ required(p.firstName);
744
+ maxLength(p.firstName, 50);
745
+ required(p.lastName);
746
+ maxLength(p.lastName, 50);
747
+ // password
748
+ required(p.password);
749
+ validate(p.password, ({ value }) => PasswordValidators.standard()(value()));
750
+ // confirmPassword + cross-field match
751
+ required(p.confirmPassword);
752
+ validate(p, ({ value }) => {
753
+ const m = value();
754
+ return m.password !== m.confirmPassword ? { kind: 'areequal' } : null;
755
+ });
756
+ }, {
757
+ submission: {
758
+ action: async () => {
759
+ if (this.busy())
760
+ return;
761
+ const m = this.formModel();
762
+ const model = {
763
+ email: m.email,
764
+ name: m.name,
765
+ firstName: m.firstName,
766
+ lastName: m.lastName,
767
+ password: m.password,
768
+ };
769
+ this.busy.set(true);
770
+ try {
771
+ await firstValueFrom(this._accountService.register(model, this.autoConfirm()));
772
+ this.busy.set(false);
773
+ this._snackbar.open($localize `:auth-jwt-admin:Registration succeeded`, 'OK');
774
+ this._userRepository.reset();
775
+ this.registered.emit();
776
+ }
777
+ catch (error) {
778
+ this.busy.set(false);
779
+ console.error('Registration error' + (error ? JSON.stringify(error) : ''));
780
+ this._snackbar.open($localize `:auth-jwt-admin:Registration error`, 'OK');
781
+ }
782
+ },
839
783
  },
840
784
  });
785
+ this.autoConfirm = input(/* @ts-ignore */
786
+ ...(ngDevMode ? [undefined, { debugName: "autoConfirm" }] : /* istanbul ignore next */ []));
787
+ this.registered = output();
788
+ this.busy = signal(undefined, /* @ts-ignore */
789
+ ...(ngDevMode ? [{ debugName: "busy" }] : /* istanbul ignore next */ []));
790
+ this.hidePassword = signal(true, /* @ts-ignore */
791
+ ...(ngDevMode ? [{ debugName: "hidePassword" }] : /* istanbul ignore next */ []));
841
792
  }
842
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: AuthJwtRegistrationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
843
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.4", type: AuthJwtRegistrationComponent, isStandalone: true, selector: "auth-jwt-registration", inputs: { autoConfirm: { classPropertyName: "autoConfirm", publicName: "autoConfirm", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { registered: "registered" }, ngImport: i0, template: "<div>\r\n <form role=\"form\" [formGroup]=\"form\" (submit)=\"onSubmit()\">\r\n <fieldset>\r\n <legend i18n=\"auth-jwt-admin\">registration</legend>\r\n <!-- email -->\r\n <div class=\"form-row\">\r\n <mat-form-field>\r\n <mat-label i18n>email</mat-label>\r\n <input\r\n matInput\r\n type=\"email\"\r\n id=\"email\"\r\n maxlength=\"256\"\r\n required\r\n autofocus\r\n spellcheck=\"false\"\r\n [formControl]=\"email\"\r\n />\r\n @if ( $any(email).errors?.required && (email.dirty || email.touched) )\r\n {\r\n <mat-error i18n=\"auth-jwt-admin\">email required</mat-error>\r\n } @if ($any(email).errors?.email && (email.dirty || email.touched)) {\r\n <mat-error i18n=\"auth-jwt-admin\">invalid email</mat-error>\r\n } @if ( $any(email).errors?.uniqueEmail && (email.dirty ||\r\n email.touched) ) {\r\n <mat-error i18n=\"auth-jwt-admin\">email already registered</mat-error>\r\n }\r\n </mat-form-field>\r\n @if (email.pending) {\r\n <mat-icon>hourglass</mat-icon>\r\n }\r\n </div>\r\n\r\n <!-- name -->\r\n <div class=\"form-row\">\r\n <mat-form-field>\r\n <mat-label i18n=\"auth-jwt-admin\">username</mat-label>\r\n <input\r\n matInput\r\n type=\"text\"\r\n id=\"name\"\r\n maxlength=\"50\"\r\n required\r\n pattern=\"^[a-zA-Z][a-zA-Z0-9]{2,49}$\"\r\n spellcheck=\"false\"\r\n [formControl]=\"name\"\r\n />\r\n @if ($any(name).errors?.required && (name.dirty || name.touched)) {\r\n <mat-error i18n=\"auth-jwt-admin\">username required</mat-error>\r\n } @if ($any(name).errors?.pattern && (name.dirty || name.touched)) {\r\n <mat-error i18n=\"auth-jwt-admin\">invalid username</mat-error>\r\n } @if ( $any(name).errors?.uniqueName && (name.dirty || name.touched)\r\n ) {\r\n <mat-error i18n=\"auth-jwt-admin\"\r\n >username already registered</mat-error\r\n >\r\n }\r\n </mat-form-field>\r\n @if (name.pending) {\r\n <mat-icon>hourglass</mat-icon>\r\n }\r\n </div>\r\n\r\n <!-- first name -->\r\n <div>\r\n <mat-form-field>\r\n <mat-label i18n=\"auth-jwt-admin\">first name</mat-label>\r\n <input\r\n matInput\r\n type=\"text\"\r\n id=\"firstName\"\r\n maxlength=\"50\"\r\n required\r\n spellcheck=\"false\"\r\n [formControl]=\"firstName\"\r\n />\r\n @if ( firstName.hasError('required') && (firstName.dirty ||\r\n firstName.touched) ) {\r\n <mat-error i18n=\"auth-jwt-admin\" class=\"text-danger small\">\r\n first name required\r\n </mat-error>\r\n }\r\n </mat-form-field>\r\n </div>\r\n\r\n <!-- last name -->\r\n <div>\r\n <mat-form-field>\r\n <mat-label i18n=\"auth-jwt-admin\">last name</mat-label>\r\n <input\r\n matInput\r\n type=\"text\"\r\n id=\"lastName\"\r\n maxlength=\"50\"\r\n required\r\n spellcheck=\"false\"\r\n [formControl]=\"lastName\"\r\n />\r\n @if ( lastName.hasError('required') && (lastName.dirty ||\r\n lastName.touched) ) {\r\n <mat-error i18n=\"auth-jwt-admin\" class=\"text-danger small\">\r\n last name required\r\n </mat-error>\r\n }\r\n </mat-form-field>\r\n </div>\r\n\r\n <!-- <div [formGroup]=\"passwords\"> -->\r\n <!-- password -->\r\n <div>\r\n <mat-form-field>\r\n <mat-label i18n=\"auth-jwt-admin\">password</mat-label>\r\n <input\r\n matInput\r\n [type]=\"hidePassword() ? 'password' : 'text'\"\r\n name=\"password\"\r\n autocomplete=\"new-password\"\r\n maxlength=\"50\"\r\n required\r\n spellcheck=\"false\"\r\n [formControl]=\"password\"\r\n />\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n matSuffix\r\n (click)=\"hidePassword.set(!hidePassword())\"\r\n i18n-aria-label=\"auth-jwt-login\"\r\n aria-label=\"Hide password\"\r\n [attr.aria-pressed]=\"hidePassword()\"\r\n >\r\n <mat-icon>{{\r\n hidePassword() ? \"visibility_off\" : \"visibility\"\r\n }}</mat-icon>\r\n </button>\r\n <auth-jwt-password-checklist\r\n [password]=\"password.value\"\r\n [username]=\"name.value\"\r\n [email]=\"email.value\"\r\n [minLength]=\"8\"\r\n [requireUppercase]=\"true\"\r\n [requireLowercase]=\"true\"\r\n [requireSymbol]=\"true\"\r\n [minLengthLabel]=\"'At least 8 characters'\"\r\n [uppercaseLabel]=\"'At least 1 uppercase letter'\"\r\n [lowercaseLabel]=\"'At least 1 lowercase letter'\"\r\n [symbolLabel]=\"'At least 1 punctuation/symbol'\">\r\n </auth-jwt-password-checklist>\r\n @if ( $any(password).errors?.required && (password.dirty ||\r\n password.touched) ) {\r\n <mat-error i18n=\"auth-jwt-admin\">password required</mat-error>\r\n } @if ( $any(password).errors?.passwordtooshort && (password.dirty ||\r\n password.touched) ) {\r\n <mat-error i18n=\"auth-jwt-admin\">at least 8 characters</mat-error>\r\n } @if ( $any(password).errors?.noupperinpassword && (password.dirty ||\r\n password.touched) ) {\r\n <mat-error i18n=\"auth-jwt-admin\"\r\n >at least 1 uppercase letter</mat-error\r\n >\r\n } @if ( $any(password).errors?.nolowerinpassword && (password.dirty ||\r\n password.touched) ) {\r\n <mat-error i18n=\"auth-jwt-admin\"\r\n >at least 1 lowercase letter</mat-error\r\n >\r\n } @if ( $any(password).errors?.nosymbolinpassword && (password.dirty\r\n || password.touched) ) {\r\n <mat-error i18n=\"auth-jwt-admin\"\r\n >at least 1 punctuation/symbol</mat-error\r\n >\r\n }\r\n </mat-form-field>\r\n </div>\r\n\r\n <!-- confirm password -->\r\n <div>\r\n <mat-form-field>\r\n <mat-label i18n=\"auth-jwt-admin\">confirm password</mat-label>\r\n <input\r\n matInput\r\n [type]=\"hidePassword() ? 'password' : 'text'\"\r\n name=\"confirmPassword\"\r\n maxlength=\"50\"\r\n required\r\n spellcheck=\"false\"\r\n [formControl]=\"confirmPassword\"\r\n />\r\n </mat-form-field>\r\n </div>\r\n @if (form.errors?.areequal && confirmPassword.touched) {\r\n <div i18n=\"auth-jwt-admin\" class=\"error\">\r\n password differs from confirmation password\r\n </div>\r\n }\r\n\r\n <button\r\n i18n=\"auth-jwt-admin\"\r\n mat-flat-button\r\n type=\"submit\"\r\n color=\"primary\"\r\n class=\"mat-primary\"\r\n [disabled]=\"\r\n form.invalid || form.pristine || busy() || name.pending || email.pending\r\n \"\r\n >\r\n register\r\n </button>\r\n @if (busy()) {\r\n <mat-progress-spinner diameter=\"20\"></mat-progress-spinner>\r\n }\r\n </fieldset>\r\n </form>\r\n\r\n <details>\r\n <summary i18n=\"auth-jwt-admin\">Hints</summary>\r\n <div class=\"info\">\r\n <p i18n=\"auth-jwt-admin\">\r\n To register a new user, you must provide his email address, choose a\r\n password, and choose a username, which must be unique (you will be\r\n notified if another user has already taken that name). The username must\r\n include only letters/digits, start with a letter, and be no shorter than\r\n 3 characters, nor longer than 50.\r\n </p>\r\n <p i18n=\"auth-jwt-admin\">\r\n To promote a decent security level, the password must include at least 8\r\n characters, uppercase and lowercase letters, digits, and punctuation\r\n (like dashes, stops, parentheses, etc.).\r\n </p>\r\n <p i18n=\"auth-jwt-admin\">\r\n Once registered, if messaging is enabled on the server side the user\r\n will receive an email message to the email address you specified; he\r\n will have to click on the provided link to complete the registration\r\n process. Otherwise, you should edit the newly created user and confirm\r\n the email registration.\r\n </p>\r\n </div>\r\n </details>\r\n</div>\r\n", styles: ["mat-form-field{width:400px}fieldset{border:1px solid var(--mat-sys-on-surface-variant);border-radius:8px;padding:16px}.form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}details{margin:8px}.info{column-width:600px}.error{color:var(--mat-sys-error)}\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.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i1.PatternValidator, selector: "[pattern][formControlName],[pattern][formControl],[pattern][ngModel]", inputs: ["pattern"] }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i3.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i6.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "component", type: PasswordChecklistComponent, selector: "auth-jwt-password-checklist", inputs: ["password", "username", "email", "hidden", "minLength", "maxLength", "requireUppercase", "requireLowercase", "requireNumber", "requireSymbol", "checkUsername", "checkEmail", "minLengthLabel", "maxLengthLabel", "uppercaseLabel", "lowercaseLabel", "numberLabel", "symbolLabel", "usernameLabel", "emailLabel", "noChecklist", "noMeter"], outputs: ["validChange"] }] }); }
793
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: AuthJwtRegistrationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
794
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.4", type: AuthJwtRegistrationComponent, isStandalone: true, selector: "auth-jwt-registration", inputs: { autoConfirm: { classPropertyName: "autoConfirm", publicName: "autoConfirm", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { registered: "registered" }, ngImport: i0, template: "<div>\n <form role=\"form\" [formRoot]=\"userForm\">\n <fieldset>\n <legend i18n=\"auth-jwt-admin\">registration</legend>\n\n <!-- email -->\n <div class=\"form-row\">\n <mat-form-field>\n <mat-label i18n>email</mat-label>\n <input\n matInput\n type=\"email\"\n id=\"email\"\n autofocus\n spellcheck=\"false\"\n [formField]=\"userForm.email\"\n />\n @if (userForm.email().getError('required') && (userForm.email().dirty() || userForm.email().touched())) {\n <mat-error i18n=\"auth-jwt-admin\">email required</mat-error>\n }\n @if (userForm.email().getError('email') && (userForm.email().dirty() || userForm.email().touched())) {\n <mat-error i18n=\"auth-jwt-admin\">invalid email</mat-error>\n }\n @if (userForm.email().getError('uniqueEmail') && (userForm.email().dirty() || userForm.email().touched())) {\n <mat-error i18n=\"auth-jwt-admin\">email already registered</mat-error>\n }\n </mat-form-field>\n @if (userForm.email().pending()) {\n <mat-icon>hourglass</mat-icon>\n }\n </div>\n\n <!-- name -->\n <div class=\"form-row\">\n <mat-form-field>\n <mat-label i18n=\"auth-jwt-admin\">username</mat-label>\n <input\n matInput\n type=\"text\"\n id=\"name\"\n spellcheck=\"false\"\n [formField]=\"userForm.name\"\n />\n @if (userForm.name().getError('required') && (userForm.name().dirty() || userForm.name().touched())) {\n <mat-error i18n=\"auth-jwt-admin\">username required</mat-error>\n }\n @if (userForm.name().getError('pattern') && (userForm.name().dirty() || userForm.name().touched())) {\n <mat-error i18n=\"auth-jwt-admin\">invalid username</mat-error>\n }\n @if (userForm.name().getError('uniqueName') && (userForm.name().dirty() || userForm.name().touched())) {\n <mat-error i18n=\"auth-jwt-admin\">username already registered</mat-error>\n }\n </mat-form-field>\n @if (userForm.name().pending()) {\n <mat-icon>hourglass</mat-icon>\n }\n </div>\n\n <!-- first name -->\n <div>\n <mat-form-field>\n <mat-label i18n=\"auth-jwt-admin\">first name</mat-label>\n <input\n matInput\n type=\"text\"\n id=\"firstName\"\n spellcheck=\"false\"\n [formField]=\"userForm.firstName\"\n />\n @if (userForm.firstName().getError('required') && (userForm.firstName().dirty() || userForm.firstName().touched())) {\n <mat-error i18n=\"auth-jwt-admin\" class=\"text-danger small\">first name required</mat-error>\n }\n </mat-form-field>\n </div>\n\n <!-- last name -->\n <div>\n <mat-form-field>\n <mat-label i18n=\"auth-jwt-admin\">last name</mat-label>\n <input\n matInput\n type=\"text\"\n id=\"lastName\"\n spellcheck=\"false\"\n [formField]=\"userForm.lastName\"\n />\n @if (userForm.lastName().getError('required') && (userForm.lastName().dirty() || userForm.lastName().touched())) {\n <mat-error i18n=\"auth-jwt-admin\" class=\"text-danger small\">last name required</mat-error>\n }\n </mat-form-field>\n </div>\n\n <!-- password -->\n <div>\n <mat-form-field>\n <mat-label i18n=\"auth-jwt-admin\">password</mat-label>\n <input\n matInput\n [type]=\"hidePassword() ? 'password' : 'text'\"\n autocomplete=\"new-password\"\n spellcheck=\"false\"\n [formField]=\"userForm.password\"\n />\n <button\n type=\"button\"\n mat-icon-button\n matSuffix\n (click)=\"hidePassword.set(!hidePassword())\"\n i18n-aria-label=\"auth-jwt-login\"\n aria-label=\"Hide password\"\n [attr.aria-pressed]=\"hidePassword()\"\n >\n <mat-icon>{{ hidePassword() ? \"visibility_off\" : \"visibility\" }}</mat-icon>\n </button>\n <auth-jwt-password-checklist\n [password]=\"formModel().password\"\n [username]=\"formModel().name\"\n [email]=\"formModel().email\"\n [minLength]=\"8\"\n [requireUppercase]=\"true\"\n [requireLowercase]=\"true\"\n [requireSymbol]=\"true\"\n [minLengthLabel]=\"'At least 8 characters'\"\n [uppercaseLabel]=\"'At least 1 uppercase letter'\"\n [lowercaseLabel]=\"'At least 1 lowercase letter'\"\n [symbolLabel]=\"'At least 1 punctuation/symbol'\"\n />\n @if (userForm.password().getError('required') && (userForm.password().dirty() || userForm.password().touched())) {\n <mat-error i18n=\"auth-jwt-admin\">password required</mat-error>\n }\n @if (userForm.password().getError('passwordtooshort') && (userForm.password().dirty() || userForm.password().touched())) {\n <mat-error i18n=\"auth-jwt-admin\">at least 8 characters</mat-error>\n }\n @if (userForm.password().getError('noupperinpassword') && (userForm.password().dirty() || userForm.password().touched())) {\n <mat-error i18n=\"auth-jwt-admin\">at least 1 uppercase letter</mat-error>\n }\n @if (userForm.password().getError('nolowerinpassword') && (userForm.password().dirty() || userForm.password().touched())) {\n <mat-error i18n=\"auth-jwt-admin\">at least 1 lowercase letter</mat-error>\n }\n @if (userForm.password().getError('nosymbolinpassword') && (userForm.password().dirty() || userForm.password().touched())) {\n <mat-error i18n=\"auth-jwt-admin\">at least 1 punctuation/symbol</mat-error>\n }\n </mat-form-field>\n </div>\n\n <!-- confirm password -->\n <div>\n <mat-form-field>\n <mat-label i18n=\"auth-jwt-admin\">confirm password</mat-label>\n <input\n matInput\n [type]=\"hidePassword() ? 'password' : 'text'\"\n autocomplete=\"new-password\"\n spellcheck=\"false\"\n [formField]=\"userForm.confirmPassword\"\n />\n </mat-form-field>\n </div>\n @if (userForm().getError('areequal') && userForm.confirmPassword().touched()) {\n <div i18n=\"auth-jwt-admin\" class=\"error\">\n password differs from confirmation password\n </div>\n }\n\n <button\n i18n=\"auth-jwt-admin\"\n mat-flat-button\n type=\"submit\"\n color=\"primary\"\n class=\"mat-primary\"\n [disabled]=\"!userForm().valid() || busy()\"\n >\n register\n </button>\n @if (busy()) {\n <mat-progress-spinner diameter=\"20\" />\n }\n </fieldset>\n </form>\n\n <details>\n <summary i18n=\"auth-jwt-admin\">Hints</summary>\n <div class=\"info\">\n <p i18n=\"auth-jwt-admin\">\n To register a new user, you must provide his email address, choose a\n password, and choose a username, which must be unique (you will be\n notified if another user has already taken that name). The username must\n include only letters/digits, start with a letter, and be no shorter than\n 3 characters, nor longer than 50.\n </p>\n <p i18n=\"auth-jwt-admin\">\n To promote a decent security level, the password must include at least 8\n characters, uppercase and lowercase letters, digits, and punctuation\n (like dashes, stops, parentheses, etc.).\n </p>\n <p i18n=\"auth-jwt-admin\">\n Once registered, if messaging is enabled on the server side the user\n will receive an email message to the email address you specified; he\n will have to click on the provided link to complete the registration\n process. Otherwise, you should edit the newly created user and confirm\n the email registration.\n </p>\n </div>\n </details>\n</div>\n", styles: ["mat-form-field{width:400px}fieldset{border:1px solid var(--mat-sys-on-surface-variant);border-radius:8px;padding:16px}.form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}details{margin:8px}.info{column-width:600px}.error{color:var(--mat-sys-error)}\n"], dependencies: [{ kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"], exportAs: ["formField"] }, { kind: "directive", type: FormRoot, selector: "form[formRoot]", inputs: ["formRoot"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i2.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i4.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i5.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "component", type: PasswordChecklistComponent, selector: "auth-jwt-password-checklist", inputs: ["password", "username", "email", "hidden", "minLength", "maxLength", "requireUppercase", "requireLowercase", "requireNumber", "requireSymbol", "checkUsername", "checkEmail", "minLengthLabel", "maxLengthLabel", "uppercaseLabel", "lowercaseLabel", "numberLabel", "symbolLabel", "usernameLabel", "emailLabel", "noChecklist", "noMeter"], outputs: ["validChange"] }], changeDetection: i0.ChangeDetectionStrategy.Eager }); }
844
795
  }
845
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: AuthJwtRegistrationComponent, decorators: [{
796
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: AuthJwtRegistrationComponent, decorators: [{
846
797
  type: Component,
847
798
  args: [{ selector: 'auth-jwt-registration', imports: [
848
- ReactiveFormsModule,
799
+ FormField,
800
+ FormRoot,
849
801
  MatButtonModule,
850
802
  MatFormFieldModule,
851
803
  MatIconModule,
852
804
  MatInputModule,
853
805
  MatProgressSpinnerModule,
854
806
  PasswordChecklistComponent,
855
- ], template: "<div>\r\n <form role=\"form\" [formGroup]=\"form\" (submit)=\"onSubmit()\">\r\n <fieldset>\r\n <legend i18n=\"auth-jwt-admin\">registration</legend>\r\n <!-- email -->\r\n <div class=\"form-row\">\r\n <mat-form-field>\r\n <mat-label i18n>email</mat-label>\r\n <input\r\n matInput\r\n type=\"email\"\r\n id=\"email\"\r\n maxlength=\"256\"\r\n required\r\n autofocus\r\n spellcheck=\"false\"\r\n [formControl]=\"email\"\r\n />\r\n @if ( $any(email).errors?.required && (email.dirty || email.touched) )\r\n {\r\n <mat-error i18n=\"auth-jwt-admin\">email required</mat-error>\r\n } @if ($any(email).errors?.email && (email.dirty || email.touched)) {\r\n <mat-error i18n=\"auth-jwt-admin\">invalid email</mat-error>\r\n } @if ( $any(email).errors?.uniqueEmail && (email.dirty ||\r\n email.touched) ) {\r\n <mat-error i18n=\"auth-jwt-admin\">email already registered</mat-error>\r\n }\r\n </mat-form-field>\r\n @if (email.pending) {\r\n <mat-icon>hourglass</mat-icon>\r\n }\r\n </div>\r\n\r\n <!-- name -->\r\n <div class=\"form-row\">\r\n <mat-form-field>\r\n <mat-label i18n=\"auth-jwt-admin\">username</mat-label>\r\n <input\r\n matInput\r\n type=\"text\"\r\n id=\"name\"\r\n maxlength=\"50\"\r\n required\r\n pattern=\"^[a-zA-Z][a-zA-Z0-9]{2,49}$\"\r\n spellcheck=\"false\"\r\n [formControl]=\"name\"\r\n />\r\n @if ($any(name).errors?.required && (name.dirty || name.touched)) {\r\n <mat-error i18n=\"auth-jwt-admin\">username required</mat-error>\r\n } @if ($any(name).errors?.pattern && (name.dirty || name.touched)) {\r\n <mat-error i18n=\"auth-jwt-admin\">invalid username</mat-error>\r\n } @if ( $any(name).errors?.uniqueName && (name.dirty || name.touched)\r\n ) {\r\n <mat-error i18n=\"auth-jwt-admin\"\r\n >username already registered</mat-error\r\n >\r\n }\r\n </mat-form-field>\r\n @if (name.pending) {\r\n <mat-icon>hourglass</mat-icon>\r\n }\r\n </div>\r\n\r\n <!-- first name -->\r\n <div>\r\n <mat-form-field>\r\n <mat-label i18n=\"auth-jwt-admin\">first name</mat-label>\r\n <input\r\n matInput\r\n type=\"text\"\r\n id=\"firstName\"\r\n maxlength=\"50\"\r\n required\r\n spellcheck=\"false\"\r\n [formControl]=\"firstName\"\r\n />\r\n @if ( firstName.hasError('required') && (firstName.dirty ||\r\n firstName.touched) ) {\r\n <mat-error i18n=\"auth-jwt-admin\" class=\"text-danger small\">\r\n first name required\r\n </mat-error>\r\n }\r\n </mat-form-field>\r\n </div>\r\n\r\n <!-- last name -->\r\n <div>\r\n <mat-form-field>\r\n <mat-label i18n=\"auth-jwt-admin\">last name</mat-label>\r\n <input\r\n matInput\r\n type=\"text\"\r\n id=\"lastName\"\r\n maxlength=\"50\"\r\n required\r\n spellcheck=\"false\"\r\n [formControl]=\"lastName\"\r\n />\r\n @if ( lastName.hasError('required') && (lastName.dirty ||\r\n lastName.touched) ) {\r\n <mat-error i18n=\"auth-jwt-admin\" class=\"text-danger small\">\r\n last name required\r\n </mat-error>\r\n }\r\n </mat-form-field>\r\n </div>\r\n\r\n <!-- <div [formGroup]=\"passwords\"> -->\r\n <!-- password -->\r\n <div>\r\n <mat-form-field>\r\n <mat-label i18n=\"auth-jwt-admin\">password</mat-label>\r\n <input\r\n matInput\r\n [type]=\"hidePassword() ? 'password' : 'text'\"\r\n name=\"password\"\r\n autocomplete=\"new-password\"\r\n maxlength=\"50\"\r\n required\r\n spellcheck=\"false\"\r\n [formControl]=\"password\"\r\n />\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n matSuffix\r\n (click)=\"hidePassword.set(!hidePassword())\"\r\n i18n-aria-label=\"auth-jwt-login\"\r\n aria-label=\"Hide password\"\r\n [attr.aria-pressed]=\"hidePassword()\"\r\n >\r\n <mat-icon>{{\r\n hidePassword() ? \"visibility_off\" : \"visibility\"\r\n }}</mat-icon>\r\n </button>\r\n <auth-jwt-password-checklist\r\n [password]=\"password.value\"\r\n [username]=\"name.value\"\r\n [email]=\"email.value\"\r\n [minLength]=\"8\"\r\n [requireUppercase]=\"true\"\r\n [requireLowercase]=\"true\"\r\n [requireSymbol]=\"true\"\r\n [minLengthLabel]=\"'At least 8 characters'\"\r\n [uppercaseLabel]=\"'At least 1 uppercase letter'\"\r\n [lowercaseLabel]=\"'At least 1 lowercase letter'\"\r\n [symbolLabel]=\"'At least 1 punctuation/symbol'\">\r\n </auth-jwt-password-checklist>\r\n @if ( $any(password).errors?.required && (password.dirty ||\r\n password.touched) ) {\r\n <mat-error i18n=\"auth-jwt-admin\">password required</mat-error>\r\n } @if ( $any(password).errors?.passwordtooshort && (password.dirty ||\r\n password.touched) ) {\r\n <mat-error i18n=\"auth-jwt-admin\">at least 8 characters</mat-error>\r\n } @if ( $any(password).errors?.noupperinpassword && (password.dirty ||\r\n password.touched) ) {\r\n <mat-error i18n=\"auth-jwt-admin\"\r\n >at least 1 uppercase letter</mat-error\r\n >\r\n } @if ( $any(password).errors?.nolowerinpassword && (password.dirty ||\r\n password.touched) ) {\r\n <mat-error i18n=\"auth-jwt-admin\"\r\n >at least 1 lowercase letter</mat-error\r\n >\r\n } @if ( $any(password).errors?.nosymbolinpassword && (password.dirty\r\n || password.touched) ) {\r\n <mat-error i18n=\"auth-jwt-admin\"\r\n >at least 1 punctuation/symbol</mat-error\r\n >\r\n }\r\n </mat-form-field>\r\n </div>\r\n\r\n <!-- confirm password -->\r\n <div>\r\n <mat-form-field>\r\n <mat-label i18n=\"auth-jwt-admin\">confirm password</mat-label>\r\n <input\r\n matInput\r\n [type]=\"hidePassword() ? 'password' : 'text'\"\r\n name=\"confirmPassword\"\r\n maxlength=\"50\"\r\n required\r\n spellcheck=\"false\"\r\n [formControl]=\"confirmPassword\"\r\n />\r\n </mat-form-field>\r\n </div>\r\n @if (form.errors?.areequal && confirmPassword.touched) {\r\n <div i18n=\"auth-jwt-admin\" class=\"error\">\r\n password differs from confirmation password\r\n </div>\r\n }\r\n\r\n <button\r\n i18n=\"auth-jwt-admin\"\r\n mat-flat-button\r\n type=\"submit\"\r\n color=\"primary\"\r\n class=\"mat-primary\"\r\n [disabled]=\"\r\n form.invalid || form.pristine || busy() || name.pending || email.pending\r\n \"\r\n >\r\n register\r\n </button>\r\n @if (busy()) {\r\n <mat-progress-spinner diameter=\"20\"></mat-progress-spinner>\r\n }\r\n </fieldset>\r\n </form>\r\n\r\n <details>\r\n <summary i18n=\"auth-jwt-admin\">Hints</summary>\r\n <div class=\"info\">\r\n <p i18n=\"auth-jwt-admin\">\r\n To register a new user, you must provide his email address, choose a\r\n password, and choose a username, which must be unique (you will be\r\n notified if another user has already taken that name). The username must\r\n include only letters/digits, start with a letter, and be no shorter than\r\n 3 characters, nor longer than 50.\r\n </p>\r\n <p i18n=\"auth-jwt-admin\">\r\n To promote a decent security level, the password must include at least 8\r\n characters, uppercase and lowercase letters, digits, and punctuation\r\n (like dashes, stops, parentheses, etc.).\r\n </p>\r\n <p i18n=\"auth-jwt-admin\">\r\n Once registered, if messaging is enabled on the server side the user\r\n will receive an email message to the email address you specified; he\r\n will have to click on the provided link to complete the registration\r\n process. Otherwise, you should edit the newly created user and confirm\r\n the email registration.\r\n </p>\r\n </div>\r\n </details>\r\n</div>\r\n", styles: ["mat-form-field{width:400px}fieldset{border:1px solid var(--mat-sys-on-surface-variant);border-radius:8px;padding:16px}.form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}details{margin:8px}.info{column-width:600px}.error{color:var(--mat-sys-error)}\n"] }]
856
- }], ctorParameters: () => [], propDecorators: { autoConfirm: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoConfirm", required: false }] }], registered: [{ type: i0.Output, args: ["registered"] }] } });
807
+ ], changeDetection: ChangeDetectionStrategy.Eager, template: "<div>\n <form role=\"form\" [formRoot]=\"userForm\">\n <fieldset>\n <legend i18n=\"auth-jwt-admin\">registration</legend>\n\n <!-- email -->\n <div class=\"form-row\">\n <mat-form-field>\n <mat-label i18n>email</mat-label>\n <input\n matInput\n type=\"email\"\n id=\"email\"\n autofocus\n spellcheck=\"false\"\n [formField]=\"userForm.email\"\n />\n @if (userForm.email().getError('required') && (userForm.email().dirty() || userForm.email().touched())) {\n <mat-error i18n=\"auth-jwt-admin\">email required</mat-error>\n }\n @if (userForm.email().getError('email') && (userForm.email().dirty() || userForm.email().touched())) {\n <mat-error i18n=\"auth-jwt-admin\">invalid email</mat-error>\n }\n @if (userForm.email().getError('uniqueEmail') && (userForm.email().dirty() || userForm.email().touched())) {\n <mat-error i18n=\"auth-jwt-admin\">email already registered</mat-error>\n }\n </mat-form-field>\n @if (userForm.email().pending()) {\n <mat-icon>hourglass</mat-icon>\n }\n </div>\n\n <!-- name -->\n <div class=\"form-row\">\n <mat-form-field>\n <mat-label i18n=\"auth-jwt-admin\">username</mat-label>\n <input\n matInput\n type=\"text\"\n id=\"name\"\n spellcheck=\"false\"\n [formField]=\"userForm.name\"\n />\n @if (userForm.name().getError('required') && (userForm.name().dirty() || userForm.name().touched())) {\n <mat-error i18n=\"auth-jwt-admin\">username required</mat-error>\n }\n @if (userForm.name().getError('pattern') && (userForm.name().dirty() || userForm.name().touched())) {\n <mat-error i18n=\"auth-jwt-admin\">invalid username</mat-error>\n }\n @if (userForm.name().getError('uniqueName') && (userForm.name().dirty() || userForm.name().touched())) {\n <mat-error i18n=\"auth-jwt-admin\">username already registered</mat-error>\n }\n </mat-form-field>\n @if (userForm.name().pending()) {\n <mat-icon>hourglass</mat-icon>\n }\n </div>\n\n <!-- first name -->\n <div>\n <mat-form-field>\n <mat-label i18n=\"auth-jwt-admin\">first name</mat-label>\n <input\n matInput\n type=\"text\"\n id=\"firstName\"\n spellcheck=\"false\"\n [formField]=\"userForm.firstName\"\n />\n @if (userForm.firstName().getError('required') && (userForm.firstName().dirty() || userForm.firstName().touched())) {\n <mat-error i18n=\"auth-jwt-admin\" class=\"text-danger small\">first name required</mat-error>\n }\n </mat-form-field>\n </div>\n\n <!-- last name -->\n <div>\n <mat-form-field>\n <mat-label i18n=\"auth-jwt-admin\">last name</mat-label>\n <input\n matInput\n type=\"text\"\n id=\"lastName\"\n spellcheck=\"false\"\n [formField]=\"userForm.lastName\"\n />\n @if (userForm.lastName().getError('required') && (userForm.lastName().dirty() || userForm.lastName().touched())) {\n <mat-error i18n=\"auth-jwt-admin\" class=\"text-danger small\">last name required</mat-error>\n }\n </mat-form-field>\n </div>\n\n <!-- password -->\n <div>\n <mat-form-field>\n <mat-label i18n=\"auth-jwt-admin\">password</mat-label>\n <input\n matInput\n [type]=\"hidePassword() ? 'password' : 'text'\"\n autocomplete=\"new-password\"\n spellcheck=\"false\"\n [formField]=\"userForm.password\"\n />\n <button\n type=\"button\"\n mat-icon-button\n matSuffix\n (click)=\"hidePassword.set(!hidePassword())\"\n i18n-aria-label=\"auth-jwt-login\"\n aria-label=\"Hide password\"\n [attr.aria-pressed]=\"hidePassword()\"\n >\n <mat-icon>{{ hidePassword() ? \"visibility_off\" : \"visibility\" }}</mat-icon>\n </button>\n <auth-jwt-password-checklist\n [password]=\"formModel().password\"\n [username]=\"formModel().name\"\n [email]=\"formModel().email\"\n [minLength]=\"8\"\n [requireUppercase]=\"true\"\n [requireLowercase]=\"true\"\n [requireSymbol]=\"true\"\n [minLengthLabel]=\"'At least 8 characters'\"\n [uppercaseLabel]=\"'At least 1 uppercase letter'\"\n [lowercaseLabel]=\"'At least 1 lowercase letter'\"\n [symbolLabel]=\"'At least 1 punctuation/symbol'\"\n />\n @if (userForm.password().getError('required') && (userForm.password().dirty() || userForm.password().touched())) {\n <mat-error i18n=\"auth-jwt-admin\">password required</mat-error>\n }\n @if (userForm.password().getError('passwordtooshort') && (userForm.password().dirty() || userForm.password().touched())) {\n <mat-error i18n=\"auth-jwt-admin\">at least 8 characters</mat-error>\n }\n @if (userForm.password().getError('noupperinpassword') && (userForm.password().dirty() || userForm.password().touched())) {\n <mat-error i18n=\"auth-jwt-admin\">at least 1 uppercase letter</mat-error>\n }\n @if (userForm.password().getError('nolowerinpassword') && (userForm.password().dirty() || userForm.password().touched())) {\n <mat-error i18n=\"auth-jwt-admin\">at least 1 lowercase letter</mat-error>\n }\n @if (userForm.password().getError('nosymbolinpassword') && (userForm.password().dirty() || userForm.password().touched())) {\n <mat-error i18n=\"auth-jwt-admin\">at least 1 punctuation/symbol</mat-error>\n }\n </mat-form-field>\n </div>\n\n <!-- confirm password -->\n <div>\n <mat-form-field>\n <mat-label i18n=\"auth-jwt-admin\">confirm password</mat-label>\n <input\n matInput\n [type]=\"hidePassword() ? 'password' : 'text'\"\n autocomplete=\"new-password\"\n spellcheck=\"false\"\n [formField]=\"userForm.confirmPassword\"\n />\n </mat-form-field>\n </div>\n @if (userForm().getError('areequal') && userForm.confirmPassword().touched()) {\n <div i18n=\"auth-jwt-admin\" class=\"error\">\n password differs from confirmation password\n </div>\n }\n\n <button\n i18n=\"auth-jwt-admin\"\n mat-flat-button\n type=\"submit\"\n color=\"primary\"\n class=\"mat-primary\"\n [disabled]=\"!userForm().valid() || busy()\"\n >\n register\n </button>\n @if (busy()) {\n <mat-progress-spinner diameter=\"20\" />\n }\n </fieldset>\n </form>\n\n <details>\n <summary i18n=\"auth-jwt-admin\">Hints</summary>\n <div class=\"info\">\n <p i18n=\"auth-jwt-admin\">\n To register a new user, you must provide his email address, choose a\n password, and choose a username, which must be unique (you will be\n notified if another user has already taken that name). The username must\n include only letters/digits, start with a letter, and be no shorter than\n 3 characters, nor longer than 50.\n </p>\n <p i18n=\"auth-jwt-admin\">\n To promote a decent security level, the password must include at least 8\n characters, uppercase and lowercase letters, digits, and punctuation\n (like dashes, stops, parentheses, etc.).\n </p>\n <p i18n=\"auth-jwt-admin\">\n Once registered, if messaging is enabled on the server side the user\n will receive an email message to the email address you specified; he\n will have to click on the provided link to complete the registration\n process. Otherwise, you should edit the newly created user and confirm\n the email registration.\n </p>\n </div>\n </details>\n</div>\n", styles: ["mat-form-field{width:400px}fieldset{border:1px solid var(--mat-sys-on-surface-variant);border-radius:8px;padding:16px}.form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}details{margin:8px}.info{column-width:600px}.error{color:var(--mat-sys-error)}\n"] }]
808
+ }], propDecorators: { autoConfirm: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoConfirm", required: false }] }], registered: [{ type: i0.Output, args: ["registered"] }] } });
857
809
 
858
810
  class PasswordStrengthBarComponent {
859
811
  constructor() {
860
812
  this._colors = ['#F00', '#F90', '#FF0', '#9F0', '#0F0'];
861
- this.passwordToCheck = input(...(ngDevMode ? [undefined, { debugName: "passwordToCheck" }] : /* istanbul ignore next */ []));
813
+ this.passwordToCheck = input(/* @ts-ignore */
814
+ ...(ngDevMode ? [undefined, { debugName: "passwordToCheck" }] : /* istanbul ignore next */ []));
862
815
  this.strengthChange = output();
863
- this.bars = signal([], ...(ngDevMode ? [{ debugName: "bars" }] : /* istanbul ignore next */ []));
816
+ this.bars = signal([], /* @ts-ignore */
817
+ ...(ngDevMode ? [{ debugName: "bars" }] : /* istanbul ignore next */ []));
864
818
  }
865
819
  measureStrength(p) {
866
820
  let force = 0;
@@ -923,12 +877,12 @@ class PasswordStrengthBarComponent {
923
877
  }
924
878
  this.bars.set(bars);
925
879
  }
926
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PasswordStrengthBarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
927
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.4", type: PasswordStrengthBarComponent, isStandalone: true, selector: "auth-jwt-password-strength-bar", inputs: { passwordToCheck: { classPropertyName: "passwordToCheck", publicName: "passwordToCheck", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { strengthChange: "strengthChange" }, usesOnChanges: true, ngImport: i0, template: "<div id=\"strength\">\r\n <small>strength:</small>\r\n <ul id=\"strengthBar\">\r\n <li class=\"point\" [style.background-color]=\"bars()[0]\"></li>\r\n <li class=\"point\" [style.background-color]=\"bars()[1]\"></li>\r\n <li class=\"point\" [style.background-color]=\"bars()[2]\"></li>\r\n <li class=\"point\" [style.background-color]=\"bars()[3]\"></li>\r\n <li class=\"point\" [style.background-color]=\"bars()[4]\"></li>\r\n </ul>\r\n</div>\r\n", styles: ["ul#strengthBar{display:inline;list-style:none;margin:0 0 0 15px;padding:0;vertical-align:2px}li.point:last{margin:0!important}li.point{background:var(--mat-sys-surface-variant);border-radius:2px;display:inline-block;height:5px;margin-right:1px;width:20px}\n"] }); }
880
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: PasswordStrengthBarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
881
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.4", type: PasswordStrengthBarComponent, isStandalone: true, selector: "auth-jwt-password-strength-bar", inputs: { passwordToCheck: { classPropertyName: "passwordToCheck", publicName: "passwordToCheck", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { strengthChange: "strengthChange" }, usesOnChanges: true, ngImport: i0, template: "<div id=\"strength\">\r\n <small>strength:</small>\r\n <ul id=\"strengthBar\">\r\n <li class=\"point\" [style.background-color]=\"bars()[0]\"></li>\r\n <li class=\"point\" [style.background-color]=\"bars()[1]\"></li>\r\n <li class=\"point\" [style.background-color]=\"bars()[2]\"></li>\r\n <li class=\"point\" [style.background-color]=\"bars()[3]\"></li>\r\n <li class=\"point\" [style.background-color]=\"bars()[4]\"></li>\r\n </ul>\r\n</div>\r\n", styles: ["ul#strengthBar{display:inline;list-style:none;margin:0 0 0 15px;padding:0;vertical-align:2px}li.point:last{margin:0!important}li.point{background:var(--mat-sys-surface-variant);border-radius:2px;display:inline-block;height:5px;margin-right:1px;width:20px}\n"], changeDetection: i0.ChangeDetectionStrategy.Eager }); }
928
882
  }
929
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PasswordStrengthBarComponent, decorators: [{
883
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: PasswordStrengthBarComponent, decorators: [{
930
884
  type: Component,
931
- args: [{ selector: 'auth-jwt-password-strength-bar', template: "<div id=\"strength\">\r\n <small>strength:</small>\r\n <ul id=\"strengthBar\">\r\n <li class=\"point\" [style.background-color]=\"bars()[0]\"></li>\r\n <li class=\"point\" [style.background-color]=\"bars()[1]\"></li>\r\n <li class=\"point\" [style.background-color]=\"bars()[2]\"></li>\r\n <li class=\"point\" [style.background-color]=\"bars()[3]\"></li>\r\n <li class=\"point\" [style.background-color]=\"bars()[4]\"></li>\r\n </ul>\r\n</div>\r\n", styles: ["ul#strengthBar{display:inline;list-style:none;margin:0 0 0 15px;padding:0;vertical-align:2px}li.point:last{margin:0!important}li.point{background:var(--mat-sys-surface-variant);border-radius:2px;display:inline-block;height:5px;margin-right:1px;width:20px}\n"] }]
885
+ args: [{ selector: 'auth-jwt-password-strength-bar', changeDetection: ChangeDetectionStrategy.Eager, template: "<div id=\"strength\">\r\n <small>strength:</small>\r\n <ul id=\"strengthBar\">\r\n <li class=\"point\" [style.background-color]=\"bars()[0]\"></li>\r\n <li class=\"point\" [style.background-color]=\"bars()[1]\"></li>\r\n <li class=\"point\" [style.background-color]=\"bars()[2]\"></li>\r\n <li class=\"point\" [style.background-color]=\"bars()[3]\"></li>\r\n <li class=\"point\" [style.background-color]=\"bars()[4]\"></li>\r\n </ul>\r\n</div>\r\n", styles: ["ul#strengthBar{display:inline;list-style:none;margin:0 0 0 15px;padding:0;vertical-align:2px}li.point:last{margin:0!important}li.point{background:var(--mat-sys-surface-variant);border-radius:2px;display:inline-block;height:5px;margin-right:1px;width:20px}\n"] }]
932
886
  }], propDecorators: { passwordToCheck: [{ type: i0.Input, args: [{ isSignal: true, alias: "passwordToCheck", required: false }] }], strengthChange: [{ type: i0.Output, args: ["strengthChange"] }] } });
933
887
 
934
888
  /**
@@ -936,73 +890,46 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImpor
936
890
  */
937
891
  class UserFilterComponent {
938
892
  constructor() {
939
- /**
940
- * The filter.
941
- */
942
- this.filter = input(...(ngDevMode ? [undefined, { debugName: "filter" }] : /* istanbul ignore next */ []));
943
- /**
944
- * Whether the filter is disabled.
945
- */
946
- this.disabled = input(...(ngDevMode ? [undefined, { debugName: "disabled" }] : /* istanbul ignore next */ []));
947
- /**
948
- * Event emitted when the filter changes.
949
- */
893
+ this.filter = input(/* @ts-ignore */
894
+ ...(ngDevMode ? [undefined, { debugName: "filter" }] : /* istanbul ignore next */ []));
895
+ this.disabled = input(/* @ts-ignore */
896
+ ...(ngDevMode ? [undefined, { debugName: "disabled" }] : /* istanbul ignore next */ []));
950
897
  this.filterChange = output();
951
- const formBuilder = inject(FormBuilder);
952
- // form
953
- this.name = formBuilder.control(null);
954
- this.form = formBuilder.group({
955
- name: this.name,
956
- });
957
- // update form when filter changes
958
- effect(() => {
959
- this.updateForm(this.filter() || undefined);
898
+ this.formModel = signal({ name: '' }, /* @ts-ignore */
899
+ ...(ngDevMode ? [{ debugName: "formModel" }] : /* istanbul ignore next */ []));
900
+ this.userForm = form(this.formModel, (p) => {
901
+ disabled(p.name, { when: () => this.disabled() ?? false });
902
+ }, {
903
+ submission: {
904
+ action: async () => {
905
+ const name = this.formModel().name.trim();
906
+ this.filterChange.emit({ name: name || undefined });
907
+ },
908
+ },
960
909
  });
961
- // disable form when disabled
962
910
  effect(() => {
963
- if (this.disabled()) {
964
- this.form.disable();
965
- }
966
- else {
967
- this.form.enable();
968
- }
911
+ const filter = this.filter();
912
+ this.formModel.set({ name: filter?.name || '' });
969
913
  });
970
914
  }
971
- updateForm(filter) {
972
- if (!filter) {
973
- this.form.reset();
974
- return;
975
- }
976
- this.name.setValue(filter.name || null);
977
- this.form.markAsPristine();
978
- }
979
- getFilter() {
980
- return {
981
- name: this.name.value?.trim(),
982
- };
983
- }
984
915
  reset() {
985
- this.form.reset();
986
- this._filter = {};
987
- this.filterChange.emit(this._filter);
988
- }
989
- apply() {
990
- this._filter = this.getFilter();
991
- this.filterChange.emit(this._filter);
916
+ this.formModel.set({ name: '' });
917
+ this.filterChange.emit({});
992
918
  }
993
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: UserFilterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
994
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.4", type: UserFilterComponent, isStandalone: true, selector: "auth-jwt-user-filter", inputs: { filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { filterChange: "filterChange" }, ngImport: i0, template: "<form [formGroup]=\"form\" (submit)=\"apply()\">\r\n <div class=\"form-row\">\r\n <mat-form-field>\r\n <mat-label i18n=\"auth-jwt-admin\">name or ID</mat-label>\r\n <input matInput [formControl]=\"name\" />\r\n <button\r\n mat-icon-button\r\n matSuffix\r\n type=\"button\"\r\n (click)=\"reset()\"\r\n i18n-matTooltip=\"auth-jwt-admin\"\r\n matTooltip=\"Reset filters\"\r\n [disabled]=\"disabled()\"\r\n >\r\n <mat-icon class=\"mat-warn\">clear</mat-icon>\r\n </button>\r\n </mat-form-field>\r\n\r\n <button\r\n id=\"apply\"\r\n type=\"submit\"\r\n mat-icon-button\r\n [disabled]=\"disabled()\"\r\n i18n-matTooltip=\"auth-jwt-admin\"\r\n matTooltip=\"Apply filters\"\r\n >\r\n <mat-icon class=\"mat-primary\">check_circle</mat-icon>\r\n </button>\r\n </div>\r\n</form>\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}#apply{margin-top:-20px}\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.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i6$1.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }] }); }
919
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: UserFilterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
920
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.4", type: UserFilterComponent, isStandalone: true, selector: "auth-jwt-user-filter", inputs: { filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { filterChange: "filterChange" }, ngImport: i0, template: "<form [formRoot]=\"userForm\">\n <div class=\"form-row\">\n <mat-form-field>\n <mat-label i18n=\"auth-jwt-admin\">name or ID</mat-label>\n <input matInput [formField]=\"userForm.name\" />\n <button\n mat-icon-button\n matSuffix\n type=\"button\"\n (click)=\"reset()\"\n i18n-matTooltip=\"auth-jwt-admin\"\n matTooltip=\"Reset filters\"\n [disabled]=\"disabled()\"\n >\n <mat-icon class=\"mat-warn\">clear</mat-icon>\n </button>\n </mat-form-field>\n\n <button\n id=\"apply\"\n type=\"submit\"\n mat-icon-button\n [disabled]=\"disabled()\"\n i18n-matTooltip=\"auth-jwt-admin\"\n matTooltip=\"Apply filters\"\n >\n <mat-icon class=\"mat-primary\">check_circle</mat-icon>\n </button>\n </div>\n</form>\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}#apply{margin-top:-20px}\n"], dependencies: [{ kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"], exportAs: ["formField"] }, { kind: "directive", type: FormRoot, selector: "form[formRoot]", inputs: ["formRoot"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i4.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i6.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }], changeDetection: i0.ChangeDetectionStrategy.Eager }); }
995
921
  }
996
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: UserFilterComponent, decorators: [{
922
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: UserFilterComponent, decorators: [{
997
923
  type: Component,
998
924
  args: [{ selector: 'auth-jwt-user-filter', imports: [
999
- ReactiveFormsModule,
925
+ FormField,
926
+ FormRoot,
1000
927
  MatButtonModule,
1001
928
  MatFormFieldModule,
1002
929
  MatIconModule,
1003
930
  MatInputModule,
1004
931
  MatTooltipModule,
1005
- ], template: "<form [formGroup]=\"form\" (submit)=\"apply()\">\r\n <div class=\"form-row\">\r\n <mat-form-field>\r\n <mat-label i18n=\"auth-jwt-admin\">name or ID</mat-label>\r\n <input matInput [formControl]=\"name\" />\r\n <button\r\n mat-icon-button\r\n matSuffix\r\n type=\"button\"\r\n (click)=\"reset()\"\r\n i18n-matTooltip=\"auth-jwt-admin\"\r\n matTooltip=\"Reset filters\"\r\n [disabled]=\"disabled()\"\r\n >\r\n <mat-icon class=\"mat-warn\">clear</mat-icon>\r\n </button>\r\n </mat-form-field>\r\n\r\n <button\r\n id=\"apply\"\r\n type=\"submit\"\r\n mat-icon-button\r\n [disabled]=\"disabled()\"\r\n i18n-matTooltip=\"auth-jwt-admin\"\r\n matTooltip=\"Apply filters\"\r\n >\r\n <mat-icon class=\"mat-primary\">check_circle</mat-icon>\r\n </button>\r\n </div>\r\n</form>\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}#apply{margin-top:-20px}\n"] }]
932
+ ], changeDetection: ChangeDetectionStrategy.Eager, template: "<form [formRoot]=\"userForm\">\n <div class=\"form-row\">\n <mat-form-field>\n <mat-label i18n=\"auth-jwt-admin\">name or ID</mat-label>\n <input matInput [formField]=\"userForm.name\" />\n <button\n mat-icon-button\n matSuffix\n type=\"button\"\n (click)=\"reset()\"\n i18n-matTooltip=\"auth-jwt-admin\"\n matTooltip=\"Reset filters\"\n [disabled]=\"disabled()\"\n >\n <mat-icon class=\"mat-warn\">clear</mat-icon>\n </button>\n </mat-form-field>\n\n <button\n id=\"apply\"\n type=\"submit\"\n mat-icon-button\n [disabled]=\"disabled()\"\n i18n-matTooltip=\"auth-jwt-admin\"\n matTooltip=\"Apply filters\"\n >\n <mat-icon class=\"mat-primary\">check_circle</mat-icon>\n </button>\n </div>\n</form>\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}#apply{margin-top:-20px}\n"] }]
1006
933
  }], ctorParameters: () => [], propDecorators: { filter: [{ type: i0.Input, args: [{ isSignal: true, alias: "filter", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], filterChange: [{ type: i0.Output, args: ["filterChange"] }] } });
1007
934
 
1008
935
  /**
@@ -1011,84 +938,74 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImpor
1011
938
  class UserEditorComponent {
1012
939
  constructor() {
1013
940
  this._authService = inject(AuthJwtService);
1014
- this.unlocked = signal(undefined, ...(ngDevMode ? [{ debugName: "unlocked" }] : /* istanbul ignore next */ []));
1015
- /**
1016
- * The user to edit.
1017
- */
1018
- this.user = input(undefined, ...(ngDevMode ? [{ debugName: "user" }] : /* istanbul ignore next */ []));
1019
- /**
1020
- * Emitted when user changes.
1021
- */
941
+ this.formModel = signal({
942
+ email: '',
943
+ emailConfirmed: false,
944
+ lockoutEnabled: false,
945
+ firstName: '',
946
+ lastName: '',
947
+ roles: '',
948
+ }, /* @ts-ignore */
949
+ ...(ngDevMode ? [{ debugName: "formModel" }] : /* istanbul ignore next */ []));
950
+ this.userForm = form(this.formModel, (p) => {
951
+ required(p.email);
952
+ email(p.email);
953
+ required(p.firstName);
954
+ maxLength(p.firstName, 50);
955
+ required(p.lastName);
956
+ maxLength(p.lastName, 50);
957
+ maxLength(p.roles, 200);
958
+ }, {
959
+ submission: {
960
+ action: async () => {
961
+ this.userChange.emit(this.getUserFromForm());
962
+ },
963
+ },
964
+ });
965
+ this.unlocked = signal(undefined, /* @ts-ignore */
966
+ ...(ngDevMode ? [{ debugName: "unlocked" }] : /* istanbul ignore next */ []));
967
+ this.user = input(undefined, /* @ts-ignore */
968
+ ...(ngDevMode ? [{ debugName: "user" }] : /* istanbul ignore next */ []));
1022
969
  this.userChange = output();
1023
- /**
1024
- * Emitted when the user requests to close the user editor.
1025
- */
1026
970
  this.editorClose = output();
1027
- const formBuilder = inject(FormBuilder);
1028
- // form
1029
- this.email = formBuilder.control(null, [
1030
- Validators.required,
1031
- Validators.email,
1032
- ]);
1033
- this.emailConfirmed = formBuilder.control(false, {
1034
- nonNullable: true,
1035
- });
1036
- this.lockoutEnabled = formBuilder.control(false, {
1037
- nonNullable: true,
1038
- });
1039
- this.firstName = formBuilder.control(null, [
1040
- Validators.required,
1041
- Validators.maxLength(50),
1042
- ]);
1043
- this.lastName = formBuilder.control(null, [
1044
- Validators.required,
1045
- Validators.maxLength(50),
1046
- ]);
1047
- this.roles = formBuilder.control(null, Validators.maxLength(200));
1048
- this.form = formBuilder.group({
1049
- email: this.email,
1050
- emailConfirmed: this.emailConfirmed,
1051
- lockoutEnabled: this.lockoutEnabled,
1052
- firstName: this.firstName,
1053
- lastName: this.lastName,
1054
- roles: this.roles,
1055
- });
1056
- // update form when user changes
1057
971
  effect(() => {
1058
972
  this.updateForm(this.user() || undefined);
1059
973
  });
1060
974
  }
1061
975
  updateForm(user) {
1062
976
  if (!user) {
1063
- this.form.reset();
977
+ this.formModel.set({
978
+ email: '',
979
+ emailConfirmed: false,
980
+ lockoutEnabled: false,
981
+ firstName: '',
982
+ lastName: '',
983
+ roles: '',
984
+ });
1064
985
  }
1065
986
  else {
1066
- this.email.setValue(user.email);
1067
- this.emailConfirmed.setValue(user.emailConfirmed || false);
1068
- this.lockoutEnabled.setValue(user.lockoutEnabled || false);
1069
- this.firstName.setValue(user.firstName || '');
1070
- this.lastName.setValue(user.lastName || '');
1071
- if (user.roles?.length > 0) {
1072
- this.roles.setValue(user.roles.join(' '));
1073
- }
1074
- else {
1075
- this.roles.setValue(null);
1076
- }
987
+ this.formModel.set({
988
+ email: user.email,
989
+ emailConfirmed: user.emailConfirmed || false,
990
+ lockoutEnabled: user.lockoutEnabled || false,
991
+ firstName: user.firstName || '',
992
+ lastName: user.lastName || '',
993
+ roles: user.roles?.length > 0 ? user.roles.join(' ') : '',
994
+ });
1077
995
  this.unlocked.set(user.lockoutEnabled &&
1078
996
  (!user.lockoutEnd || user.lockoutEnd < this._authService.getUTCDate()));
1079
997
  }
1080
998
  }
1081
999
  getUserFromForm() {
1000
+ const m = this.formModel();
1082
1001
  return {
1083
1002
  userName: this.user()?.userName || '',
1084
- email: this.email.value || '',
1085
- emailConfirmed: this.emailConfirmed.value,
1086
- lockoutEnabled: this.lockoutEnabled.value,
1087
- firstName: this.firstName.value || '',
1088
- lastName: this.lastName.value || '',
1089
- roles: this.roles.value
1090
- ? this.roles.value.split(' ').filter((s) => s)
1091
- : [],
1003
+ email: m.email || '',
1004
+ emailConfirmed: m.emailConfirmed,
1005
+ lockoutEnabled: m.lockoutEnabled,
1006
+ firstName: m.firstName || '',
1007
+ lastName: m.lastName || '',
1008
+ roles: m.roles.trim() ? m.roles.split(' ').filter((s) => s) : [],
1092
1009
  };
1093
1010
  }
1094
1011
  endLockout() {
@@ -1102,26 +1019,21 @@ class UserEditorComponent {
1102
1019
  close() {
1103
1020
  this.editorClose.emit();
1104
1021
  }
1105
- save() {
1106
- if (this.form.invalid) {
1107
- return;
1108
- }
1109
- this.userChange.emit(this.getUserFromForm());
1110
- }
1111
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: UserEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1112
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.4", type: UserEditorComponent, isStandalone: true, selector: "auth-jwt-user-editor", inputs: { user: { classPropertyName: "user", publicName: "user", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { userChange: "userChange", editorClose: "editorClose" }, ngImport: i0, template: "<form [formGroup]=\"form\" (submit)=\"save()\">\r\n <div class=\"form-row\">\r\n <!-- email -->\r\n <mat-form-field>\r\n <mat-label i18n=\"auth-jwt-admin\">email</mat-label>\r\n <input type=\"text\" matInput [formControl]=\"email\" />\r\n @if (email.hasError('required') && (email.dirty || email.touched)) {\r\n <mat-error i18n=\"auth-jwt-admin\"> email address required </mat-error>\r\n } @if (email.hasError('pattern') && (email.dirty || email.touched)) {\r\n <mat-error i18n=\"auth-jwt-admin\"> invalid email address </mat-error>\r\n }\r\n </mat-form-field>\r\n\r\n <!-- emailConfirmed -->\r\n <mat-checkbox [formControl]=\"emailConfirmed\" i18n=\"auth-jwt-admin\"\r\n >email address confirmed</mat-checkbox\r\n >\r\n </div>\r\n\r\n <!-- lockoutEnabled -->\r\n <div class=\"form-row\">\r\n <mat-checkbox [formControl]=\"lockoutEnabled\" i18n=\"auth-jwt-admin\"\r\n >lockout enabled</mat-checkbox\r\n >\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n (click)=\"endLockout()\"\r\n [disabled]=\"unlocked()\"\r\n i18n-matTooltip=\"auth-jwt-admin\"\r\n matTooltip=\"Unlock this user if locked\"\r\n >\r\n <mat-icon class=\"mat-primary\">lock_open</mat-icon>\r\n </button>\r\n </div>\r\n\r\n <div class=\"form-row\">\r\n <!-- firstName -->\r\n <mat-form-field>\r\n <mat-label i18n=\"auth-jwt-admin\">first name</mat-label>\r\n <input type=\"text\" matInput [formControl]=\"firstName\" />\r\n @if ( firstName.hasError('required') && (firstName.dirty ||\r\n firstName.touched) ) {\r\n <mat-error i18n=\"auth-jwt-admin\"> first name required </mat-error>\r\n } @if ( firstName.hasError('maxlength') && (firstName.dirty ||\r\n firstName.touched) ) {\r\n <mat-error i18n=\"auth-jwt-admin\"> first name too long </mat-error>\r\n }\r\n </mat-form-field>\r\n\r\n <!-- lastName -->\r\n <mat-form-field>\r\n <mat-label i18n=\"auth-jwt-admin\">last name</mat-label>\r\n <input type=\"text\" matInput [formControl]=\"lastName\" />\r\n @if ( lastName.hasError('required') && (lastName.dirty ||\r\n lastName.touched) ) {\r\n <mat-error i18n=\"auth-jwt-admin\"> last name required </mat-error>\r\n } @if ( lastName.hasError('maxlength') && (lastName.dirty ||\r\n lastName.touched) ) {\r\n <mat-error i18n=\"auth-jwt-admin\"> last name too long </mat-error>\r\n }\r\n </mat-form-field>\r\n </div>\r\n\r\n <!-- roles -->\r\n <div>\r\n <mat-form-field>\r\n <mat-label i18n=\"auth-jwt-admin\">roles</mat-label>\r\n <input type=\"text\" matInput [formControl]=\"roles\" />\r\n @if (roles.hasError('maxlength') && (roles.dirty || roles.touched)) {\r\n <mat-error i18n=\"auth-jwt-admin\"> too long </mat-error>\r\n }\r\n <mat-hint i18n>roles (separated by space)</mat-hint>\r\n </mat-form-field>\r\n </div>\r\n\r\n <!-- buttons -->\r\n <br />\r\n <div>\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n i18n-matTooltip=\"auth-jwt-admin\"\r\n matTooltip=\"Close\"\r\n (click)=\"close()\"\r\n >\r\n <mat-icon class=\"mat-warn\">cancel</mat-icon>\r\n </button>\r\n <button\r\n type=\"submit\"\r\n mat-icon-button\r\n color=\"primary\"\r\n [disabled]=\"form.invalid\"\r\n i18n-matTooltip=\"auth-jwt-admin\"\r\n matTooltip=\"Save user\"\r\n >\r\n <mat-icon class=\"mat-primary\">check_circle</mat-icon>\r\n </button>\r\n </div>\r\n</form>\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}\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.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i3$1.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: i3.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i6$1.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }] }); }
1022
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: UserEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1023
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.4", type: UserEditorComponent, isStandalone: true, selector: "auth-jwt-user-editor", inputs: { user: { classPropertyName: "user", publicName: "user", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { userChange: "userChange", editorClose: "editorClose" }, ngImport: i0, template: "<form [formRoot]=\"userForm\">\n <div class=\"form-row\">\n <!-- email -->\n <mat-form-field>\n <mat-label i18n=\"auth-jwt-admin\">email</mat-label>\n <input type=\"text\" matInput [formField]=\"userForm.email\" />\n @if (userForm.email().getError('required') && (userForm.email().dirty() || userForm.email().touched())) {\n <mat-error i18n=\"auth-jwt-admin\">email address required</mat-error>\n }\n @if (userForm.email().getError('email') && (userForm.email().dirty() || userForm.email().touched())) {\n <mat-error i18n=\"auth-jwt-admin\">invalid email address</mat-error>\n }\n </mat-form-field>\n\n <!-- emailConfirmed -->\n <mat-checkbox [formField]=\"userForm.emailConfirmed\" i18n=\"auth-jwt-admin\">\n email address confirmed\n </mat-checkbox>\n </div>\n\n <!-- lockoutEnabled -->\n <div class=\"form-row\">\n <mat-checkbox [formField]=\"userForm.lockoutEnabled\" i18n=\"auth-jwt-admin\">\n lockout enabled\n </mat-checkbox>\n <button\n type=\"button\"\n mat-icon-button\n (click)=\"endLockout()\"\n [disabled]=\"unlocked()\"\n i18n-matTooltip=\"auth-jwt-admin\"\n matTooltip=\"Unlock this user if locked\"\n >\n <mat-icon class=\"mat-primary\">lock_open</mat-icon>\n </button>\n </div>\n\n <div class=\"form-row\">\n <!-- firstName -->\n <mat-form-field>\n <mat-label i18n=\"auth-jwt-admin\">first name</mat-label>\n <input type=\"text\" matInput [formField]=\"userForm.firstName\" />\n @if (userForm.firstName().getError('required') && (userForm.firstName().dirty() || userForm.firstName().touched())) {\n <mat-error i18n=\"auth-jwt-admin\">first name required</mat-error>\n }\n @if (userForm.firstName().getError('maxLength') && (userForm.firstName().dirty() || userForm.firstName().touched())) {\n <mat-error i18n=\"auth-jwt-admin\">first name too long</mat-error>\n }\n </mat-form-field>\n\n <!-- lastName -->\n <mat-form-field>\n <mat-label i18n=\"auth-jwt-admin\">last name</mat-label>\n <input type=\"text\" matInput [formField]=\"userForm.lastName\" />\n @if (userForm.lastName().getError('required') && (userForm.lastName().dirty() || userForm.lastName().touched())) {\n <mat-error i18n=\"auth-jwt-admin\">last name required</mat-error>\n }\n @if (userForm.lastName().getError('maxLength') && (userForm.lastName().dirty() || userForm.lastName().touched())) {\n <mat-error i18n=\"auth-jwt-admin\">last name too long</mat-error>\n }\n </mat-form-field>\n </div>\n\n <!-- roles -->\n <div>\n <mat-form-field>\n <mat-label i18n=\"auth-jwt-admin\">roles</mat-label>\n <input type=\"text\" matInput [formField]=\"userForm.roles\" />\n @if (userForm.roles().getError('maxLength') && (userForm.roles().dirty() || userForm.roles().touched())) {\n <mat-error i18n=\"auth-jwt-admin\">too long</mat-error>\n }\n <mat-hint i18n>roles (separated by space)</mat-hint>\n </mat-form-field>\n </div>\n\n <!-- buttons -->\n <br />\n <div>\n <button\n type=\"button\"\n mat-icon-button\n i18n-matTooltip=\"auth-jwt-admin\"\n matTooltip=\"Close\"\n (click)=\"close()\"\n >\n <mat-icon class=\"mat-warn\">cancel</mat-icon>\n </button>\n <button\n type=\"submit\"\n mat-icon-button\n color=\"primary\"\n [disabled]=\"!userForm().valid()\"\n i18n-matTooltip=\"auth-jwt-admin\"\n matTooltip=\"Save user\"\n >\n <mat-icon class=\"mat-primary\">check_circle</mat-icon>\n </button>\n </div>\n</form>\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}\n"], dependencies: [{ kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"], exportAs: ["formField"] }, { kind: "directive", type: FormRoot, selector: "form[formRoot]", inputs: ["formRoot"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i2$1.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: i2.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i4.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i6.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }], changeDetection: i0.ChangeDetectionStrategy.Eager }); }
1113
1024
  }
1114
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: UserEditorComponent, decorators: [{
1025
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: UserEditorComponent, decorators: [{
1115
1026
  type: Component,
1116
1027
  args: [{ selector: 'auth-jwt-user-editor', imports: [
1117
- ReactiveFormsModule,
1028
+ FormField,
1029
+ FormRoot,
1118
1030
  MatButtonModule,
1119
1031
  MatCheckboxModule,
1120
1032
  MatFormFieldModule,
1121
1033
  MatIconModule,
1122
1034
  MatInputModule,
1123
1035
  MatTooltipModule,
1124
- ], template: "<form [formGroup]=\"form\" (submit)=\"save()\">\r\n <div class=\"form-row\">\r\n <!-- email -->\r\n <mat-form-field>\r\n <mat-label i18n=\"auth-jwt-admin\">email</mat-label>\r\n <input type=\"text\" matInput [formControl]=\"email\" />\r\n @if (email.hasError('required') && (email.dirty || email.touched)) {\r\n <mat-error i18n=\"auth-jwt-admin\"> email address required </mat-error>\r\n } @if (email.hasError('pattern') && (email.dirty || email.touched)) {\r\n <mat-error i18n=\"auth-jwt-admin\"> invalid email address </mat-error>\r\n }\r\n </mat-form-field>\r\n\r\n <!-- emailConfirmed -->\r\n <mat-checkbox [formControl]=\"emailConfirmed\" i18n=\"auth-jwt-admin\"\r\n >email address confirmed</mat-checkbox\r\n >\r\n </div>\r\n\r\n <!-- lockoutEnabled -->\r\n <div class=\"form-row\">\r\n <mat-checkbox [formControl]=\"lockoutEnabled\" i18n=\"auth-jwt-admin\"\r\n >lockout enabled</mat-checkbox\r\n >\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n (click)=\"endLockout()\"\r\n [disabled]=\"unlocked()\"\r\n i18n-matTooltip=\"auth-jwt-admin\"\r\n matTooltip=\"Unlock this user if locked\"\r\n >\r\n <mat-icon class=\"mat-primary\">lock_open</mat-icon>\r\n </button>\r\n </div>\r\n\r\n <div class=\"form-row\">\r\n <!-- firstName -->\r\n <mat-form-field>\r\n <mat-label i18n=\"auth-jwt-admin\">first name</mat-label>\r\n <input type=\"text\" matInput [formControl]=\"firstName\" />\r\n @if ( firstName.hasError('required') && (firstName.dirty ||\r\n firstName.touched) ) {\r\n <mat-error i18n=\"auth-jwt-admin\"> first name required </mat-error>\r\n } @if ( firstName.hasError('maxlength') && (firstName.dirty ||\r\n firstName.touched) ) {\r\n <mat-error i18n=\"auth-jwt-admin\"> first name too long </mat-error>\r\n }\r\n </mat-form-field>\r\n\r\n <!-- lastName -->\r\n <mat-form-field>\r\n <mat-label i18n=\"auth-jwt-admin\">last name</mat-label>\r\n <input type=\"text\" matInput [formControl]=\"lastName\" />\r\n @if ( lastName.hasError('required') && (lastName.dirty ||\r\n lastName.touched) ) {\r\n <mat-error i18n=\"auth-jwt-admin\"> last name required </mat-error>\r\n } @if ( lastName.hasError('maxlength') && (lastName.dirty ||\r\n lastName.touched) ) {\r\n <mat-error i18n=\"auth-jwt-admin\"> last name too long </mat-error>\r\n }\r\n </mat-form-field>\r\n </div>\r\n\r\n <!-- roles -->\r\n <div>\r\n <mat-form-field>\r\n <mat-label i18n=\"auth-jwt-admin\">roles</mat-label>\r\n <input type=\"text\" matInput [formControl]=\"roles\" />\r\n @if (roles.hasError('maxlength') && (roles.dirty || roles.touched)) {\r\n <mat-error i18n=\"auth-jwt-admin\"> too long </mat-error>\r\n }\r\n <mat-hint i18n>roles (separated by space)</mat-hint>\r\n </mat-form-field>\r\n </div>\r\n\r\n <!-- buttons -->\r\n <br />\r\n <div>\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n i18n-matTooltip=\"auth-jwt-admin\"\r\n matTooltip=\"Close\"\r\n (click)=\"close()\"\r\n >\r\n <mat-icon class=\"mat-warn\">cancel</mat-icon>\r\n </button>\r\n <button\r\n type=\"submit\"\r\n mat-icon-button\r\n color=\"primary\"\r\n [disabled]=\"form.invalid\"\r\n i18n-matTooltip=\"auth-jwt-admin\"\r\n matTooltip=\"Save user\"\r\n >\r\n <mat-icon class=\"mat-primary\">check_circle</mat-icon>\r\n </button>\r\n </div>\r\n</form>\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}\n"] }]
1036
+ ], changeDetection: ChangeDetectionStrategy.Eager, template: "<form [formRoot]=\"userForm\">\n <div class=\"form-row\">\n <!-- email -->\n <mat-form-field>\n <mat-label i18n=\"auth-jwt-admin\">email</mat-label>\n <input type=\"text\" matInput [formField]=\"userForm.email\" />\n @if (userForm.email().getError('required') && (userForm.email().dirty() || userForm.email().touched())) {\n <mat-error i18n=\"auth-jwt-admin\">email address required</mat-error>\n }\n @if (userForm.email().getError('email') && (userForm.email().dirty() || userForm.email().touched())) {\n <mat-error i18n=\"auth-jwt-admin\">invalid email address</mat-error>\n }\n </mat-form-field>\n\n <!-- emailConfirmed -->\n <mat-checkbox [formField]=\"userForm.emailConfirmed\" i18n=\"auth-jwt-admin\">\n email address confirmed\n </mat-checkbox>\n </div>\n\n <!-- lockoutEnabled -->\n <div class=\"form-row\">\n <mat-checkbox [formField]=\"userForm.lockoutEnabled\" i18n=\"auth-jwt-admin\">\n lockout enabled\n </mat-checkbox>\n <button\n type=\"button\"\n mat-icon-button\n (click)=\"endLockout()\"\n [disabled]=\"unlocked()\"\n i18n-matTooltip=\"auth-jwt-admin\"\n matTooltip=\"Unlock this user if locked\"\n >\n <mat-icon class=\"mat-primary\">lock_open</mat-icon>\n </button>\n </div>\n\n <div class=\"form-row\">\n <!-- firstName -->\n <mat-form-field>\n <mat-label i18n=\"auth-jwt-admin\">first name</mat-label>\n <input type=\"text\" matInput [formField]=\"userForm.firstName\" />\n @if (userForm.firstName().getError('required') && (userForm.firstName().dirty() || userForm.firstName().touched())) {\n <mat-error i18n=\"auth-jwt-admin\">first name required</mat-error>\n }\n @if (userForm.firstName().getError('maxLength') && (userForm.firstName().dirty() || userForm.firstName().touched())) {\n <mat-error i18n=\"auth-jwt-admin\">first name too long</mat-error>\n }\n </mat-form-field>\n\n <!-- lastName -->\n <mat-form-field>\n <mat-label i18n=\"auth-jwt-admin\">last name</mat-label>\n <input type=\"text\" matInput [formField]=\"userForm.lastName\" />\n @if (userForm.lastName().getError('required') && (userForm.lastName().dirty() || userForm.lastName().touched())) {\n <mat-error i18n=\"auth-jwt-admin\">last name required</mat-error>\n }\n @if (userForm.lastName().getError('maxLength') && (userForm.lastName().dirty() || userForm.lastName().touched())) {\n <mat-error i18n=\"auth-jwt-admin\">last name too long</mat-error>\n }\n </mat-form-field>\n </div>\n\n <!-- roles -->\n <div>\n <mat-form-field>\n <mat-label i18n=\"auth-jwt-admin\">roles</mat-label>\n <input type=\"text\" matInput [formField]=\"userForm.roles\" />\n @if (userForm.roles().getError('maxLength') && (userForm.roles().dirty() || userForm.roles().touched())) {\n <mat-error i18n=\"auth-jwt-admin\">too long</mat-error>\n }\n <mat-hint i18n>roles (separated by space)</mat-hint>\n </mat-form-field>\n </div>\n\n <!-- buttons -->\n <br />\n <div>\n <button\n type=\"button\"\n mat-icon-button\n i18n-matTooltip=\"auth-jwt-admin\"\n matTooltip=\"Close\"\n (click)=\"close()\"\n >\n <mat-icon class=\"mat-warn\">cancel</mat-icon>\n </button>\n <button\n type=\"submit\"\n mat-icon-button\n color=\"primary\"\n [disabled]=\"!userForm().valid()\"\n i18n-matTooltip=\"auth-jwt-admin\"\n matTooltip=\"Save user\"\n >\n <mat-icon class=\"mat-primary\">check_circle</mat-icon>\n </button>\n </div>\n</form>\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}\n"] }]
1125
1037
  }], ctorParameters: () => [], propDecorators: { user: [{ type: i0.Input, args: [{ isSignal: true, alias: "user", required: false }] }], userChange: [{ type: i0.Output, args: ["userChange"] }], editorClose: [{ type: i0.Output, args: ["editorClose"] }] } });
1126
1038
 
1127
1039
  /**
@@ -1174,14 +1086,13 @@ class UserListComponent {
1174
1086
  getGravatarUrl(email, options) {
1175
1087
  return this._gravatarService.buildGravatarUrl(email, options);
1176
1088
  }
1177
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: UserListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1178
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.4", type: UserListComponent, isStandalone: true, selector: "auth-jwt-user-list", ngImport: i0, template: "<div id=\"container\">\n <div>\n <!-- filters -->\n <div id=\"filters\">\n <auth-jwt-user-filter\n [filter]=\"filter$ | async\"\n (filterChange)=\"onFilterChange($event)\"\n ></auth-jwt-user-filter>\n </div>\n\n <!-- list -->\n @if (page$ | async; as page) {\n <div id=\"list\">\n @if (loading$ | async) {\n <div>\n <mat-progress-bar mode=\"indeterminate\"></mat-progress-bar>\n </div>\n }\n <table>\n <thead>\n <td></td>\n <td></td>\n <th i18n=\"auth-jwt-admin\">name</th>\n <th i18n=\"auth-jwt-admin\" class=\"noif-lt-md\">first</th>\n <th i18n=\"auth-jwt-admin\" class=\"noif-lt-md\">last</th>\n <th i18n=\"auth-jwt-admin\" class=\"noif-lt-md\">email</th>\n <th i18n=\"auth-jwt-admin\">roles</th>\n <th i18n=\"auth-jwt-admin\" class=\"noif-lt-md\">lock end</th>\n </thead>\n <tbody>\n @for (user of page.items; track user) {\n <tr>\n <td class=\"fit-width\">\n <button\n mat-icon-button\n type=\"button\"\n i18n-matTooltip=\"auth-jwt-admin\"\n matTooltip=\"Edit this user\"\n color=\"primary\"\n (click)=\"setActiveUser(user)\"\n >\n <mat-icon class=\"mat-primary\">mode_edit</mat-icon>\n </button>\n <button\n mat-icon-button\n type=\"button\"\n i18n-matTooltip=\"auth-jwt-admin\"\n matTooltip=\"Delete this user\"\n color=\"warn\"\n (click)=\"deleteUser(user)\"\n >\n <mat-icon class=\"mat-warn\">remove_circle</mat-icon>\n </button>\n </td>\n <td class=\"fit-width\">\n <img\n alt=\"avatar\"\n [src]=\"getGravatarUrl(user.email, { size: '32' })\"\n [alt]=\"user.userName\"\n />\n </td>\n <td>{{ user.userName }}</td>\n <td class=\"noif-lt-md\">{{ user.firstName }}</td>\n <td class=\"noif-lt-md\">{{ user.lastName }}</td>\n <td class=\"noif-lt-md\">\n <a [href]=\"'mailto:' + user.email\">{{ user.email }}</a>\n </td>\n <td>{{ user.roles.join(\" \") }}</td>\n <td class=\"noif-lt-md\">{{ user.lockoutEnd }}</td>\n </tr>\n }\n </tbody>\n </table>\n <!-- paginator -->\n <div class=\"form-row\">\n <button\n type=\"button\"\n mat-icon-button\n color=\"warn\"\n i18n-matmatTooltip=\"auth-jwt-admin\"\n matTooltip=\"Refresh list\"\n (click)=\"reset()\"\n >\n <mat-icon class=\"mat-warn\">autorenew</mat-icon>\n </button>\n <mat-paginator\n [length]=\"page.total\"\n [pageIndex]=\"page.pageNumber - 1\"\n [pageSize]=\"page.pageSize\"\n [pageSizeOptions]=\"[5, 10, 20, 50, 100]\"\n (page)=\"onPageChange($event)\"\n [showFirstLastButtons]=\"true\"\n ></mat-paginator>\n </div>\n </div>\n }\n </div>\n\n <!-- editor -->\n <mat-expansion-panel\n id=\"editor\"\n [expanded]=\"active$ | async\"\n [disabled]=\"!(active$ | async)\"\n >\n @if (active$ | async; as active) {\n <div>\n <fieldset>\n <legend>{{ active.userName }}</legend>\n <auth-jwt-user-editor\n [user]=\"active\"\n (userChange)=\"saveActiveUser($event)\"\n (editorClose)=\"resetActiveUser()\"\n ></auth-jwt-user-editor>\n </fieldset>\n </div>\n }\n </mat-expansion-panel>\n</div>\n", styles: ["table{width:100%;border-collapse:collapse}tbody tr:nth-child(odd){background-color:var(--mat-sys-surface-container)}th{text-align:left;font-weight:400;color:var(--mat-sys-on-surface-variant)}tbody tr:hover{background-color:var(--mat-sys-surface-container-high)}td.fit-width{width:1px;white-space:nowrap}tbody tr.selected{background-color:var(--mat-sys-secondary-container);color:var(--mat-sys-on-secondary-container)}fieldset{border:1px solid var(--mat-sys-on-surface-variant);border-radius:8px;padding:16px}.form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}div#filters{grid-area:filters}div#list{grid-area:list}div#editor{grid-area:editor}div#container{display:grid;grid-template-rows:auto 1fr auto;grid-template-columns:1fr;grid-template-areas:\"filters\" \"list\" \"editor\";gap:8px}@media only screen and (max-width:959px){.noif-lt-md{display:none}}@media only screen and (min-width:1920px){div#container{grid-template-rows:auto 1fr;grid-template-columns:1fr auto;grid-template-areas:\"filters filters\" \"list editor\"}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "component", type: i2$1.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "ngmodule", type: MatPaginatorModule }, { kind: "component", type: i4$1.MatPaginator, selector: "mat-paginator", inputs: ["color", "pageIndex", "length", "pageSize", "pageSizeOptions", "hidePageSize", "showFirstLastButtons", "selectConfig", "disabled"], outputs: ["page"], exportAs: ["matPaginator"] }, { kind: "ngmodule", type: MatProgressBarModule }, { kind: "component", type: i5$1.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i6$1.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: UserEditorComponent, selector: "auth-jwt-user-editor", inputs: ["user"], outputs: ["userChange", "editorClose"] }, { kind: "component", type: UserFilterComponent, selector: "auth-jwt-user-filter", inputs: ["filter", "disabled"], outputs: ["filterChange"] }, { kind: "pipe", type: i7.AsyncPipe, name: "async" }] }); }
1089
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: UserListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1090
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.4", type: UserListComponent, isStandalone: true, selector: "auth-jwt-user-list", ngImport: i0, template: "<div id=\"container\">\n <div>\n <!-- filters -->\n <div id=\"filters\">\n <auth-jwt-user-filter\n [filter]=\"filter$ | async\"\n (filterChange)=\"onFilterChange($event)\"\n ></auth-jwt-user-filter>\n </div>\n\n <!-- list -->\n @if (page$ | async; as page) {\n <div id=\"list\">\n @if (loading$ | async) {\n <div>\n <mat-progress-bar mode=\"indeterminate\"></mat-progress-bar>\n </div>\n }\n <table>\n <thead>\n <td></td>\n <td></td>\n <th i18n=\"auth-jwt-admin\">name</th>\n <th i18n=\"auth-jwt-admin\" class=\"noif-lt-md\">first</th>\n <th i18n=\"auth-jwt-admin\" class=\"noif-lt-md\">last</th>\n <th i18n=\"auth-jwt-admin\" class=\"noif-lt-md\">email</th>\n <th i18n=\"auth-jwt-admin\">roles</th>\n <th i18n=\"auth-jwt-admin\" class=\"noif-lt-md\">lock end</th>\n </thead>\n <tbody>\n @for (user of page.items; track user) {\n <tr>\n <td class=\"fit-width\">\n <button\n mat-icon-button\n type=\"button\"\n i18n-matTooltip=\"auth-jwt-admin\"\n matTooltip=\"Edit this user\"\n color=\"primary\"\n (click)=\"setActiveUser(user)\"\n >\n <mat-icon class=\"mat-primary\">mode_edit</mat-icon>\n </button>\n <button\n mat-icon-button\n type=\"button\"\n i18n-matTooltip=\"auth-jwt-admin\"\n matTooltip=\"Delete this user\"\n color=\"warn\"\n (click)=\"deleteUser(user)\"\n >\n <mat-icon class=\"mat-warn\">remove_circle</mat-icon>\n </button>\n </td>\n <td class=\"fit-width\">\n <img\n alt=\"avatar\"\n [src]=\"getGravatarUrl(user.email, { size: '32' })\"\n [alt]=\"user.userName\"\n />\n </td>\n <td>{{ user.userName }}</td>\n <td class=\"noif-lt-md\">{{ user.firstName }}</td>\n <td class=\"noif-lt-md\">{{ user.lastName }}</td>\n <td class=\"noif-lt-md\">\n <a [href]=\"'mailto:' + user.email\">{{ user.email }}</a>\n </td>\n <td>{{ user.roles.join(\" \") }}</td>\n <td class=\"noif-lt-md\">{{ user.lockoutEnd }}</td>\n </tr>\n }\n </tbody>\n </table>\n <!-- paginator -->\n <div class=\"form-row\">\n <button\n type=\"button\"\n mat-icon-button\n color=\"warn\"\n i18n-matmatTooltip=\"auth-jwt-admin\"\n matTooltip=\"Refresh list\"\n (click)=\"reset()\"\n >\n <mat-icon class=\"mat-warn\">autorenew</mat-icon>\n </button>\n <mat-paginator\n [length]=\"page.total\"\n [pageIndex]=\"page.pageNumber - 1\"\n [pageSize]=\"page.pageSize\"\n [pageSizeOptions]=\"[5, 10, 20, 50, 100]\"\n (page)=\"onPageChange($event)\"\n [showFirstLastButtons]=\"true\"\n ></mat-paginator>\n </div>\n </div>\n }\n </div>\n\n <!-- editor -->\n <mat-expansion-panel\n id=\"editor\"\n [expanded]=\"active$ | async\"\n [disabled]=\"!(active$ | async)\"\n >\n @if (active$ | async; as active) {\n <div>\n <fieldset>\n <legend>{{ active.userName }}</legend>\n <auth-jwt-user-editor\n [user]=\"active\"\n (userChange)=\"saveActiveUser($event)\"\n (editorClose)=\"resetActiveUser()\"\n ></auth-jwt-user-editor>\n </fieldset>\n </div>\n }\n </mat-expansion-panel>\n</div>\n", styles: ["table{width:100%;border-collapse:collapse}tbody tr:nth-child(odd){background-color:var(--mat-sys-surface-container)}th{text-align:left;font-weight:400;color:var(--mat-sys-on-surface-variant)}tbody tr:hover{background-color:var(--mat-sys-surface-container-high)}td.fit-width{width:1px;white-space:nowrap}tbody tr.selected{background-color:var(--mat-sys-secondary-container);color:var(--mat-sys-on-secondary-container)}fieldset{border:1px solid var(--mat-sys-on-surface-variant);border-radius:8px;padding:16px}.form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}div#filters{grid-area:filters}div#list{grid-area:list}div#editor{grid-area:editor}div#container{display:grid;grid-template-rows:auto 1fr auto;grid-template-columns:1fr;grid-template-areas:\"filters\" \"list\" \"editor\";gap:8px}@media only screen and (max-width:959px){.noif-lt-md{display:none}}@media only screen and (min-width:1920px){div#container{grid-template-rows:auto 1fr;grid-template-columns:1fr auto;grid-template-areas:\"filters filters\" \"list editor\"}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "component", type: i2$2.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "ngmodule", type: MatPaginatorModule }, { kind: "component", type: i4$1.MatPaginator, selector: "mat-paginator", inputs: ["color", "pageIndex", "length", "pageSize", "pageSizeOptions", "hidePageSize", "showFirstLastButtons", "selectConfig", "disabled"], outputs: ["page"], exportAs: ["matPaginator"] }, { kind: "ngmodule", type: MatProgressBarModule }, { kind: "component", type: i5$1.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i6.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: UserEditorComponent, selector: "auth-jwt-user-editor", inputs: ["user"], outputs: ["userChange", "editorClose"] }, { kind: "component", type: UserFilterComponent, selector: "auth-jwt-user-filter", inputs: ["filter", "disabled"], outputs: ["filterChange"] }, { kind: "pipe", type: i7.AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.Eager }); }
1179
1091
  }
1180
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: UserListComponent, decorators: [{
1092
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: UserListComponent, decorators: [{
1181
1093
  type: Component,
1182
1094
  args: [{ selector: 'auth-jwt-user-list', imports: [
1183
1095
  CommonModule,
1184
- ReactiveFormsModule,
1185
1096
  MatButtonModule,
1186
1097
  MatExpansionModule,
1187
1098
  MatIconModule,
@@ -1191,7 +1102,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImpor
1191
1102
  MatTooltipModule,
1192
1103
  UserEditorComponent,
1193
1104
  UserFilterComponent,
1194
- ], template: "<div id=\"container\">\n <div>\n <!-- filters -->\n <div id=\"filters\">\n <auth-jwt-user-filter\n [filter]=\"filter$ | async\"\n (filterChange)=\"onFilterChange($event)\"\n ></auth-jwt-user-filter>\n </div>\n\n <!-- list -->\n @if (page$ | async; as page) {\n <div id=\"list\">\n @if (loading$ | async) {\n <div>\n <mat-progress-bar mode=\"indeterminate\"></mat-progress-bar>\n </div>\n }\n <table>\n <thead>\n <td></td>\n <td></td>\n <th i18n=\"auth-jwt-admin\">name</th>\n <th i18n=\"auth-jwt-admin\" class=\"noif-lt-md\">first</th>\n <th i18n=\"auth-jwt-admin\" class=\"noif-lt-md\">last</th>\n <th i18n=\"auth-jwt-admin\" class=\"noif-lt-md\">email</th>\n <th i18n=\"auth-jwt-admin\">roles</th>\n <th i18n=\"auth-jwt-admin\" class=\"noif-lt-md\">lock end</th>\n </thead>\n <tbody>\n @for (user of page.items; track user) {\n <tr>\n <td class=\"fit-width\">\n <button\n mat-icon-button\n type=\"button\"\n i18n-matTooltip=\"auth-jwt-admin\"\n matTooltip=\"Edit this user\"\n color=\"primary\"\n (click)=\"setActiveUser(user)\"\n >\n <mat-icon class=\"mat-primary\">mode_edit</mat-icon>\n </button>\n <button\n mat-icon-button\n type=\"button\"\n i18n-matTooltip=\"auth-jwt-admin\"\n matTooltip=\"Delete this user\"\n color=\"warn\"\n (click)=\"deleteUser(user)\"\n >\n <mat-icon class=\"mat-warn\">remove_circle</mat-icon>\n </button>\n </td>\n <td class=\"fit-width\">\n <img\n alt=\"avatar\"\n [src]=\"getGravatarUrl(user.email, { size: '32' })\"\n [alt]=\"user.userName\"\n />\n </td>\n <td>{{ user.userName }}</td>\n <td class=\"noif-lt-md\">{{ user.firstName }}</td>\n <td class=\"noif-lt-md\">{{ user.lastName }}</td>\n <td class=\"noif-lt-md\">\n <a [href]=\"'mailto:' + user.email\">{{ user.email }}</a>\n </td>\n <td>{{ user.roles.join(\" \") }}</td>\n <td class=\"noif-lt-md\">{{ user.lockoutEnd }}</td>\n </tr>\n }\n </tbody>\n </table>\n <!-- paginator -->\n <div class=\"form-row\">\n <button\n type=\"button\"\n mat-icon-button\n color=\"warn\"\n i18n-matmatTooltip=\"auth-jwt-admin\"\n matTooltip=\"Refresh list\"\n (click)=\"reset()\"\n >\n <mat-icon class=\"mat-warn\">autorenew</mat-icon>\n </button>\n <mat-paginator\n [length]=\"page.total\"\n [pageIndex]=\"page.pageNumber - 1\"\n [pageSize]=\"page.pageSize\"\n [pageSizeOptions]=\"[5, 10, 20, 50, 100]\"\n (page)=\"onPageChange($event)\"\n [showFirstLastButtons]=\"true\"\n ></mat-paginator>\n </div>\n </div>\n }\n </div>\n\n <!-- editor -->\n <mat-expansion-panel\n id=\"editor\"\n [expanded]=\"active$ | async\"\n [disabled]=\"!(active$ | async)\"\n >\n @if (active$ | async; as active) {\n <div>\n <fieldset>\n <legend>{{ active.userName }}</legend>\n <auth-jwt-user-editor\n [user]=\"active\"\n (userChange)=\"saveActiveUser($event)\"\n (editorClose)=\"resetActiveUser()\"\n ></auth-jwt-user-editor>\n </fieldset>\n </div>\n }\n </mat-expansion-panel>\n</div>\n", styles: ["table{width:100%;border-collapse:collapse}tbody tr:nth-child(odd){background-color:var(--mat-sys-surface-container)}th{text-align:left;font-weight:400;color:var(--mat-sys-on-surface-variant)}tbody tr:hover{background-color:var(--mat-sys-surface-container-high)}td.fit-width{width:1px;white-space:nowrap}tbody tr.selected{background-color:var(--mat-sys-secondary-container);color:var(--mat-sys-on-secondary-container)}fieldset{border:1px solid var(--mat-sys-on-surface-variant);border-radius:8px;padding:16px}.form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}div#filters{grid-area:filters}div#list{grid-area:list}div#editor{grid-area:editor}div#container{display:grid;grid-template-rows:auto 1fr auto;grid-template-columns:1fr;grid-template-areas:\"filters\" \"list\" \"editor\";gap:8px}@media only screen and (max-width:959px){.noif-lt-md{display:none}}@media only screen and (min-width:1920px){div#container{grid-template-rows:auto 1fr;grid-template-columns:1fr auto;grid-template-areas:\"filters filters\" \"list editor\"}}\n"] }]
1105
+ ], changeDetection: ChangeDetectionStrategy.Eager, template: "<div id=\"container\">\n <div>\n <!-- filters -->\n <div id=\"filters\">\n <auth-jwt-user-filter\n [filter]=\"filter$ | async\"\n (filterChange)=\"onFilterChange($event)\"\n ></auth-jwt-user-filter>\n </div>\n\n <!-- list -->\n @if (page$ | async; as page) {\n <div id=\"list\">\n @if (loading$ | async) {\n <div>\n <mat-progress-bar mode=\"indeterminate\"></mat-progress-bar>\n </div>\n }\n <table>\n <thead>\n <td></td>\n <td></td>\n <th i18n=\"auth-jwt-admin\">name</th>\n <th i18n=\"auth-jwt-admin\" class=\"noif-lt-md\">first</th>\n <th i18n=\"auth-jwt-admin\" class=\"noif-lt-md\">last</th>\n <th i18n=\"auth-jwt-admin\" class=\"noif-lt-md\">email</th>\n <th i18n=\"auth-jwt-admin\">roles</th>\n <th i18n=\"auth-jwt-admin\" class=\"noif-lt-md\">lock end</th>\n </thead>\n <tbody>\n @for (user of page.items; track user) {\n <tr>\n <td class=\"fit-width\">\n <button\n mat-icon-button\n type=\"button\"\n i18n-matTooltip=\"auth-jwt-admin\"\n matTooltip=\"Edit this user\"\n color=\"primary\"\n (click)=\"setActiveUser(user)\"\n >\n <mat-icon class=\"mat-primary\">mode_edit</mat-icon>\n </button>\n <button\n mat-icon-button\n type=\"button\"\n i18n-matTooltip=\"auth-jwt-admin\"\n matTooltip=\"Delete this user\"\n color=\"warn\"\n (click)=\"deleteUser(user)\"\n >\n <mat-icon class=\"mat-warn\">remove_circle</mat-icon>\n </button>\n </td>\n <td class=\"fit-width\">\n <img\n alt=\"avatar\"\n [src]=\"getGravatarUrl(user.email, { size: '32' })\"\n [alt]=\"user.userName\"\n />\n </td>\n <td>{{ user.userName }}</td>\n <td class=\"noif-lt-md\">{{ user.firstName }}</td>\n <td class=\"noif-lt-md\">{{ user.lastName }}</td>\n <td class=\"noif-lt-md\">\n <a [href]=\"'mailto:' + user.email\">{{ user.email }}</a>\n </td>\n <td>{{ user.roles.join(\" \") }}</td>\n <td class=\"noif-lt-md\">{{ user.lockoutEnd }}</td>\n </tr>\n }\n </tbody>\n </table>\n <!-- paginator -->\n <div class=\"form-row\">\n <button\n type=\"button\"\n mat-icon-button\n color=\"warn\"\n i18n-matmatTooltip=\"auth-jwt-admin\"\n matTooltip=\"Refresh list\"\n (click)=\"reset()\"\n >\n <mat-icon class=\"mat-warn\">autorenew</mat-icon>\n </button>\n <mat-paginator\n [length]=\"page.total\"\n [pageIndex]=\"page.pageNumber - 1\"\n [pageSize]=\"page.pageSize\"\n [pageSizeOptions]=\"[5, 10, 20, 50, 100]\"\n (page)=\"onPageChange($event)\"\n [showFirstLastButtons]=\"true\"\n ></mat-paginator>\n </div>\n </div>\n }\n </div>\n\n <!-- editor -->\n <mat-expansion-panel\n id=\"editor\"\n [expanded]=\"active$ | async\"\n [disabled]=\"!(active$ | async)\"\n >\n @if (active$ | async; as active) {\n <div>\n <fieldset>\n <legend>{{ active.userName }}</legend>\n <auth-jwt-user-editor\n [user]=\"active\"\n (userChange)=\"saveActiveUser($event)\"\n (editorClose)=\"resetActiveUser()\"\n ></auth-jwt-user-editor>\n </fieldset>\n </div>\n }\n </mat-expansion-panel>\n</div>\n", styles: ["table{width:100%;border-collapse:collapse}tbody tr:nth-child(odd){background-color:var(--mat-sys-surface-container)}th{text-align:left;font-weight:400;color:var(--mat-sys-on-surface-variant)}tbody tr:hover{background-color:var(--mat-sys-surface-container-high)}td.fit-width{width:1px;white-space:nowrap}tbody tr.selected{background-color:var(--mat-sys-secondary-container);color:var(--mat-sys-on-secondary-container)}fieldset{border:1px solid var(--mat-sys-on-surface-variant);border-radius:8px;padding:16px}.form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}div#filters{grid-area:filters}div#list{grid-area:list}div#editor{grid-area:editor}div#container{display:grid;grid-template-rows:auto 1fr auto;grid-template-columns:1fr;grid-template-areas:\"filters\" \"list\" \"editor\";gap:8px}@media only screen and (max-width:959px){.noif-lt-md{display:none}}@media only screen and (min-width:1920px){div#container{grid-template-rows:auto 1fr;grid-template-columns:1fr auto;grid-template-areas:\"filters filters\" \"list editor\"}}\n"] }]
1195
1106
  }], ctorParameters: () => [] });
1196
1107
 
1197
1108
  /*