@myrmidon/auth-jwt-admin 8.0.0 → 8.0.3

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,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { inject, Injectable, input, output, Component, effect } from '@angular/core';
2
+ import { inject, Injectable, input, output, computed, effect, ChangeDetectionStrategy, Component, signal } from '@angular/core';
3
3
  import * as i1 from '@angular/forms';
4
4
  import { FormBuilder, Validators, ReactiveFormsModule } from '@angular/forms';
5
5
  import { MatSnackBar } from '@angular/material/snack-bar';
@@ -18,15 +18,15 @@ import { HttpClient, HttpParams } from '@angular/common/http';
18
18
  import { ErrorService, EnvService } from '@myrmidon/ngx-tools';
19
19
  import { BehaviorSubject, tap } from 'rxjs';
20
20
  import { PagedListStore } from '@myrmidon/paged-data-browsers';
21
- import * as i7 from '@angular/material/tooltip';
21
+ import * as i6$1 from '@angular/material/tooltip';
22
22
  import { MatTooltipModule } from '@angular/material/tooltip';
23
- import * as i5$1 from '@angular/material/paginator';
23
+ import * as i4$1 from '@angular/material/paginator';
24
24
  import { MatPaginatorModule } from '@angular/material/paginator';
25
- import * as i1$1 from '@angular/common';
25
+ import * as i7 from '@angular/common';
26
26
  import { CommonModule } from '@angular/common';
27
- import * as i3$2 from '@angular/material/expansion';
27
+ import * as i2$1 from '@angular/material/expansion';
28
28
  import { MatExpansionModule } from '@angular/material/expansion';
29
- import * as i6$1 from '@angular/material/progress-bar';
29
+ import * as i5$1 from '@angular/material/progress-bar';
30
30
  import { MatProgressBarModule } from '@angular/material/progress-bar';
31
31
  import { AuthJwtService, GravatarService } from '@myrmidon/auth-jwt-login';
32
32
  import { DialogService } from '@myrmidon/ngx-mat-tools';
@@ -223,10 +223,10 @@ class AuthJwtAccountService {
223
223
  .delete(this._env.get('apiUrl') + 'accounts/' + name)
224
224
  .pipe(catchError(this._error.handleError));
225
225
  }
226
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.0", ngImport: i0, type: AuthJwtAccountService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
227
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.0", ngImport: i0, type: AuthJwtAccountService, providedIn: 'root' }); }
226
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: AuthJwtAccountService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
227
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: AuthJwtAccountService, providedIn: 'root' }); }
228
228
  }
229
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImport: i0, type: AuthJwtAccountService, decorators: [{
229
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: AuthJwtAccountService, decorators: [{
230
230
  type: Injectable,
231
231
  args: [{
232
232
  providedIn: 'root',
@@ -398,87 +398,290 @@ class UserListRepository {
398
398
  });
399
399
  return promise;
400
400
  }
401
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.0", ngImport: i0, type: UserListRepository, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
402
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.0", ngImport: i0, type: UserListRepository, providedIn: 'root' }); }
401
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: UserListRepository, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
402
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: UserListRepository, providedIn: 'root' }); }
403
403
  }
404
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImport: i0, type: UserListRepository, decorators: [{
404
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: UserListRepository, decorators: [{
405
405
  type: Injectable,
406
406
  args: [{ providedIn: 'root' }]
407
407
  }], ctorParameters: () => [] });
408
408
 
409
- class PasswordStrengthBarComponent {
409
+ /**
410
+ * Default labels for password rules.
411
+ */
412
+ const DEFAULT_RULE_LABELS = {
413
+ minLength: 'At least {value} characters',
414
+ maxLength: 'No more than {value} characters',
415
+ hasUppercase: 'Contains uppercase letter',
416
+ hasLowercase: 'Contains lowercase letter',
417
+ hasNumber: 'Contains number',
418
+ hasSymbol: 'Contains symbol',
419
+ noUsername: 'Does not contain username',
420
+ noEmail: 'Does not contain email',
421
+ };
422
+ /**
423
+ * A modern, fully reactive password checklist component with strength meter.
424
+ * Uses only signals for state management and is ready for zoneless Angular.
425
+ */
426
+ class PasswordChecklistComponent {
410
427
  constructor() {
411
- this._colors = ['#F00', '#F90', '#FF0', '#9F0', '#0F0'];
412
- this.passwordToCheck = input();
413
- this.strengthChange = output();
414
- this.bars = [];
415
- }
416
- measureStrength(p) {
417
- let force = 0;
418
- const regex = /[$-/:-?{-~!"^_`\[\]]/g; // "
419
- const lowerLetters = /[a-z]+/.test(p);
420
- const upperLetters = /[A-Z]+/.test(p);
421
- const numbers = /[0-9]+/.test(p);
422
- const symbols = regex.test(p);
423
- const flags = [lowerLetters, upperLetters, numbers, symbols];
424
- let passedMatches = 0;
425
- for (const flag of flags) {
426
- passedMatches += flag === true ? 1 : 0;
427
- }
428
- force += 2 * p.length + (p.length >= 10 ? 1 : 0);
429
- force += passedMatches * 10;
430
- // penalty (short password)
431
- force = p.length <= 6 ? Math.min(force, 10) : force;
432
- // penalty (poor letiety of characters)
433
- force = passedMatches === 1 ? Math.min(force, 10) : force;
434
- force = passedMatches === 2 ? Math.min(force, 20) : force;
435
- force = passedMatches === 3 ? Math.min(force, 40) : force;
436
- return force;
428
+ // Input signals for password and context
429
+ this.password = input('', ...(ngDevMode ? [{ debugName: "password" }] : []));
430
+ this.username = input('', ...(ngDevMode ? [{ debugName: "username" }] : []));
431
+ this.email = input('', ...(ngDevMode ? [{ debugName: "email" }] : []));
432
+ this.hidden = input(false, ...(ngDevMode ? [{ debugName: "hidden" }] : []));
433
+ // Input signals for rule configuration
434
+ this.minLength = input(null, ...(ngDevMode ? [{ debugName: "minLength" }] : []));
435
+ this.maxLength = input(null, ...(ngDevMode ? [{ debugName: "maxLength" }] : []));
436
+ this.requireUppercase = input(false, ...(ngDevMode ? [{ debugName: "requireUppercase" }] : []));
437
+ this.requireLowercase = input(false, ...(ngDevMode ? [{ debugName: "requireLowercase" }] : []));
438
+ this.requireNumber = input(false, ...(ngDevMode ? [{ debugName: "requireNumber" }] : []));
439
+ this.requireSymbol = input(false, ...(ngDevMode ? [{ debugName: "requireSymbol" }] : []));
440
+ this.checkUsername = input(false, ...(ngDevMode ? [{ debugName: "checkUsername" }] : []));
441
+ this.checkEmail = input(false, ...(ngDevMode ? [{ debugName: "checkEmail" }] : []));
442
+ // Input signals for custom labels
443
+ this.minLengthLabel = input(null, ...(ngDevMode ? [{ debugName: "minLengthLabel" }] : []));
444
+ this.maxLengthLabel = input(null, ...(ngDevMode ? [{ debugName: "maxLengthLabel" }] : []));
445
+ this.uppercaseLabel = input(null, ...(ngDevMode ? [{ debugName: "uppercaseLabel" }] : []));
446
+ this.lowercaseLabel = input(null, ...(ngDevMode ? [{ debugName: "lowercaseLabel" }] : []));
447
+ this.numberLabel = input(null, ...(ngDevMode ? [{ debugName: "numberLabel" }] : []));
448
+ this.symbolLabel = input(null, ...(ngDevMode ? [{ debugName: "symbolLabel" }] : []));
449
+ this.usernameLabel = input(null, ...(ngDevMode ? [{ debugName: "usernameLabel" }] : []));
450
+ this.emailLabel = input(null, ...(ngDevMode ? [{ debugName: "emailLabel" }] : []));
451
+ // Input signals for display options
452
+ this.noChecklist = input(false, ...(ngDevMode ? [{ debugName: "noChecklist" }] : []));
453
+ this.noMeter = input(false, ...(ngDevMode ? [{ debugName: "noMeter" }] : []));
454
+ // Output signal for validity
455
+ this.validChange = output();
456
+ // Symbol regex pattern
457
+ this.symbolRegex = /[-`~!@#$%^&*()_+=\[\]{};:'",.<>/?|\\]/;
458
+ // Computed signals for individual rule checks
459
+ this.hasMinLength = computed(() => {
460
+ const min = this.minLength();
461
+ return min === null || this.password().length >= min;
462
+ }, ...(ngDevMode ? [{ debugName: "hasMinLength" }] : []));
463
+ this.hasMaxLength = computed(() => {
464
+ const max = this.maxLength();
465
+ return max === null || this.password().length <= max;
466
+ }, ...(ngDevMode ? [{ debugName: "hasMaxLength" }] : []));
467
+ this.hasUppercase = computed(() => {
468
+ return /[A-Z]/.test(this.password());
469
+ }, ...(ngDevMode ? [{ debugName: "hasUppercase" }] : []));
470
+ this.hasLowercase = computed(() => {
471
+ return /[a-z]/.test(this.password());
472
+ }, ...(ngDevMode ? [{ debugName: "hasLowercase" }] : []));
473
+ this.hasNumber = computed(() => {
474
+ return /[0-9]/.test(this.password());
475
+ }, ...(ngDevMode ? [{ debugName: "hasNumber" }] : []));
476
+ this.hasSymbol = computed(() => {
477
+ return this.symbolRegex.test(this.password());
478
+ }, ...(ngDevMode ? [{ debugName: "hasSymbol" }] : []));
479
+ this.noUsernameInPassword = computed(() => {
480
+ const username = this.username().toLowerCase();
481
+ const pwd = this.password().toLowerCase();
482
+ return !username || !pwd || !pwd.includes(username);
483
+ }, ...(ngDevMode ? [{ debugName: "noUsernameInPassword" }] : []));
484
+ this.noEmailInPassword = computed(() => {
485
+ const email = this.email().toLowerCase();
486
+ const pwd = this.password().toLowerCase();
487
+ return !email || !pwd || !pwd.includes(email);
488
+ }, ...(ngDevMode ? [{ debugName: "noEmailInPassword" }] : []));
489
+ // Computed signal for all rules with their status
490
+ this.rules = computed(() => {
491
+ const rules = [];
492
+ if (this.minLength() !== null) {
493
+ rules.push({
494
+ id: 'minLength',
495
+ label: this.getLabel('minLength', this.minLengthLabel(), this.minLength()),
496
+ enabled: true,
497
+ });
498
+ }
499
+ if (this.maxLength() !== null) {
500
+ rules.push({
501
+ id: 'maxLength',
502
+ label: this.getLabel('maxLength', this.maxLengthLabel(), this.maxLength()),
503
+ enabled: true,
504
+ });
505
+ }
506
+ if (this.requireUppercase()) {
507
+ rules.push({
508
+ id: 'hasUppercase',
509
+ label: this.getLabel('hasUppercase', this.uppercaseLabel()),
510
+ enabled: true,
511
+ });
512
+ }
513
+ if (this.requireLowercase()) {
514
+ rules.push({
515
+ id: 'hasLowercase',
516
+ label: this.getLabel('hasLowercase', this.lowercaseLabel()),
517
+ enabled: true,
518
+ });
519
+ }
520
+ if (this.requireNumber()) {
521
+ rules.push({
522
+ id: 'hasNumber',
523
+ label: this.getLabel('hasNumber', this.numberLabel()),
524
+ enabled: true,
525
+ });
526
+ }
527
+ if (this.requireSymbol()) {
528
+ rules.push({
529
+ id: 'hasSymbol',
530
+ label: this.getLabel('hasSymbol', this.symbolLabel()),
531
+ enabled: true,
532
+ });
533
+ }
534
+ if (this.checkUsername()) {
535
+ rules.push({
536
+ id: 'noUsername',
537
+ label: this.getLabel('noUsername', this.usernameLabel()),
538
+ enabled: true,
539
+ });
540
+ }
541
+ if (this.checkEmail()) {
542
+ rules.push({
543
+ id: 'noEmail',
544
+ label: this.getLabel('noEmail', this.emailLabel()),
545
+ enabled: true,
546
+ });
547
+ }
548
+ return rules;
549
+ }, ...(ngDevMode ? [{ debugName: "rules" }] : []));
550
+ // Computed signal to check if a specific rule is satisfied
551
+ this.isRuleSatisfied = computed(() => {
552
+ return (ruleId) => {
553
+ switch (ruleId) {
554
+ case 'minLength':
555
+ return this.hasMinLength();
556
+ case 'maxLength':
557
+ return this.hasMaxLength();
558
+ case 'hasUppercase':
559
+ return this.hasUppercase();
560
+ case 'hasLowercase':
561
+ return this.hasLowercase();
562
+ case 'hasNumber':
563
+ return this.hasNumber();
564
+ case 'hasSymbol':
565
+ return this.hasSymbol();
566
+ case 'noUsername':
567
+ return this.noUsernameInPassword();
568
+ case 'noEmail':
569
+ return this.noEmailInPassword();
570
+ default:
571
+ return false;
572
+ }
573
+ };
574
+ }, ...(ngDevMode ? [{ debugName: "isRuleSatisfied" }] : []));
575
+ // Computed signal for overall validity
576
+ this.isValid = computed(() => {
577
+ const rules = this.rules();
578
+ if (rules.length === 0) {
579
+ return true;
580
+ }
581
+ const checkFn = this.isRuleSatisfied();
582
+ return rules.every((rule) => checkFn(rule.id));
583
+ }, ...(ngDevMode ? [{ debugName: "isValid" }] : []));
584
+ // Computed signal for password strength (0-100)
585
+ this.strength = computed(() => {
586
+ const pwd = this.password();
587
+ if (!pwd) {
588
+ return 0;
589
+ }
590
+ let score = 0;
591
+ // Length contribution (max 30 points)
592
+ const lengthScore = Math.min(pwd.length * 2, 30);
593
+ score += lengthScore;
594
+ // Character variety (max 40 points, 10 per type)
595
+ const hasLower = /[a-z]/.test(pwd);
596
+ const hasUpper = /[A-Z]/.test(pwd);
597
+ const hasDigit = /[0-9]/.test(pwd);
598
+ const hasSymbol = this.symbolRegex.test(pwd);
599
+ score += hasLower ? 10 : 0;
600
+ score += hasUpper ? 10 : 0;
601
+ score += hasDigit ? 10 : 0;
602
+ score += hasSymbol ? 10 : 0;
603
+ // Entropy bonus (max 30 points)
604
+ const uniqueChars = new Set(pwd).size;
605
+ const entropyScore = Math.min((uniqueChars / pwd.length) * 30, 30);
606
+ score += entropyScore;
607
+ // Penalties
608
+ if (pwd.length < 8) {
609
+ score = Math.min(score, 20);
610
+ }
611
+ const varietyCount = [hasLower, hasUpper, hasDigit, hasSymbol].filter(Boolean).length;
612
+ if (varietyCount === 1) {
613
+ score = Math.min(score, 20);
614
+ }
615
+ else if (varietyCount === 2) {
616
+ score = Math.min(score, 40);
617
+ }
618
+ else if (varietyCount === 3) {
619
+ score = Math.min(score, 70);
620
+ }
621
+ return Math.min(Math.round(score), 100);
622
+ }, ...(ngDevMode ? [{ debugName: "strength" }] : []));
623
+ // Computed signal for strength level (0-4)
624
+ this.strengthLevel = computed(() => {
625
+ const strength = this.strength();
626
+ if (strength <= 20)
627
+ return 0;
628
+ if (strength <= 40)
629
+ return 1;
630
+ if (strength <= 60)
631
+ return 2;
632
+ if (strength <= 80)
633
+ return 3;
634
+ return 4;
635
+ }, ...(ngDevMode ? [{ debugName: "strengthLevel" }] : []));
636
+ // Computed signal for strength color
637
+ this.strengthColor = computed(() => {
638
+ const colors = ['#F00', '#F90', '#FF0', '#9F0', '#0F0'];
639
+ return colors[this.strengthLevel()];
640
+ }, ...(ngDevMode ? [{ debugName: "strengthColor" }] : []));
641
+ // Computed signal for strength percentage
642
+ this.strengthPercent = computed(() => {
643
+ return this.strength();
644
+ }, ...(ngDevMode ? [{ debugName: "strengthPercent" }] : []));
645
+ // Effect to emit validity changes
646
+ // This will automatically track the isValid computed signal
647
+ effect(() => {
648
+ const isValid = this.isValid();
649
+ // Emit the validity status whenever it changes
650
+ this.validChange.emit(isValid);
651
+ });
437
652
  }
438
- getColor(s) {
439
- let idx = 0;
440
- if (s <= 10) {
441
- idx = 0;
442
- }
443
- else if (s <= 20) {
444
- idx = 1;
445
- }
446
- else if (s <= 30) {
447
- idx = 2;
448
- }
449
- else if (s <= 40) {
450
- idx = 3;
451
- }
452
- else {
453
- idx = 4;
653
+ /**
654
+ * Gets the label for a rule, using custom label if provided, otherwise default.
655
+ */
656
+ getLabel(ruleId, customLabel, value) {
657
+ if (customLabel) {
658
+ return this.interpolateLabel(customLabel, value);
454
659
  }
455
- return {
456
- idx: idx + 1,
457
- col: this._colors[idx],
458
- };
660
+ const defaultLabel = DEFAULT_RULE_LABELS[ruleId] || ruleId;
661
+ return this.interpolateLabel(defaultLabel, value);
459
662
  }
460
- ngOnChanges(changes) {
461
- const password = changes['passwordToCheck'].currentValue;
462
- this.setBarColors(5, '#DDD');
463
- if (password) {
464
- const strength = this.measureStrength(password);
465
- const c = this.getColor(strength);
466
- this.setBarColors(c.idx, c.col);
467
- this.strengthChange.emit(strength);
663
+ /**
664
+ * Interpolates {value} placeholder in label.
665
+ */
666
+ interpolateLabel(label, value) {
667
+ if (value !== undefined) {
668
+ return label.replace('{value}', value.toString());
468
669
  }
670
+ return label;
469
671
  }
470
- setBarColors(count, col) {
471
- for (let n = 0; n < count; n++) {
472
- this.bars[n] = col;
473
- }
672
+ /**
673
+ * Helper method to check if a rule is satisfied (for template use).
674
+ */
675
+ checkRule(ruleId) {
676
+ return this.isRuleSatisfied()(ruleId);
474
677
  }
475
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.0", ngImport: i0, type: PasswordStrengthBarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
476
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.0.0", 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\">\n <small>strength:</small>\n <ul id=\"strengthBar\">\n <li class=\"point\" [style.background-color]=\"bars[0]\"></li>\n <li class=\"point\" [style.background-color]=\"bars[1]\"></li>\n <li class=\"point\" [style.background-color]=\"bars[2]\"></li>\n <li class=\"point\" [style.background-color]=\"bars[3]\"></li>\n <li class=\"point\" [style.background-color]=\"bars[4]\"></li>\n </ul>\n</div>\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:#ddd;border-radius:2px;display:inline-block;height:5px;margin-right:1px;width:20px}\n"] }); }
678
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: PasswordChecklistComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
679
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.0", 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:#ddd;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:#4caf50;color:#fff}.checklist-item.unsatisfied .checklist-icon{background-color:#f44336;color:#fff}.checklist-text{flex:1}.checklist-item.satisfied .checklist-text{color:#4caf50}.checklist-item.unsatisfied .checklist-text{color:#f44336}@media(prefers-color-scheme:dark){.strength-bar-container{background-color:#444}.checklist-item.satisfied .checklist-text{color:#81c784}.checklist-item.unsatisfied .checklist-text{color:#e57373}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
477
680
  }
478
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImport: i0, type: PasswordStrengthBarComponent, decorators: [{
681
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: PasswordChecklistComponent, decorators: [{
479
682
  type: Component,
480
- args: [{ selector: 'auth-jwt-password-strength-bar', template: "<div id=\"strength\">\n <small>strength:</small>\n <ul id=\"strengthBar\">\n <li class=\"point\" [style.background-color]=\"bars[0]\"></li>\n <li class=\"point\" [style.background-color]=\"bars[1]\"></li>\n <li class=\"point\" [style.background-color]=\"bars[2]\"></li>\n <li class=\"point\" [style.background-color]=\"bars[3]\"></li>\n <li class=\"point\" [style.background-color]=\"bars[4]\"></li>\n </ul>\n</div>\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:#ddd;border-radius:2px;display:inline-block;height:5px;margin-right:1px;width:20px}\n"] }]
481
- }] });
683
+ 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:#ddd;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:#4caf50;color:#fff}.checklist-item.unsatisfied .checklist-icon{background-color:#f44336;color:#fff}.checklist-text{flex:1}.checklist-item.satisfied .checklist-text{color:#4caf50}.checklist-item.unsatisfied .checklist-text{color:#f44336}@media(prefers-color-scheme:dark){.strength-bar-container{background-color:#444}.checklist-item.satisfied .checklist-text{color:#81c784}.checklist-item.unsatisfied .checklist-text{color:#e57373}}\n"] }]
684
+ }], 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"] }] } });
482
685
 
483
686
  /**
484
687
  * User registration form.
@@ -491,14 +694,15 @@ class AuthJwtRegistrationComponent {
491
694
  /**
492
695
  * True if the registration should be confirmed automatically.
493
696
  */
494
- this.autoConfirm = input();
697
+ this.autoConfirm = input(...(ngDevMode ? [undefined, { debugName: "autoConfirm" }] : []));
495
698
  /**
496
699
  * Emitted when a user was successfully registered. Usually
497
700
  * you should handle this to move away from the registration
498
701
  * page.
499
702
  */
500
703
  this.registered = output();
501
- this.hide = true;
704
+ this.busy = signal(undefined, ...(ngDevMode ? [{ debugName: "busy" }] : []));
705
+ this.hidePassword = signal(true, ...(ngDevMode ? [{ debugName: "hidePassword" }] : []));
502
706
  const formBuilder = inject(FormBuilder);
503
707
  // form
504
708
  this.email = formBuilder.control('', {
@@ -605,7 +809,7 @@ class AuthJwtRegistrationComponent {
605
809
  }
606
810
  onSubmit() {
607
811
  if (!this.form.valid ||
608
- this.busy ||
812
+ this.busy() ||
609
813
  this.name.pending ||
610
814
  this.email.pending) {
611
815
  return;
@@ -617,28 +821,28 @@ class AuthJwtRegistrationComponent {
617
821
  lastName: this.lastName.value,
618
822
  password: this.password.value,
619
823
  };
620
- this.busy = true;
824
+ this.busy.set(true);
621
825
  this._accountService
622
826
  .register(model, this.autoConfirm())
623
827
  .pipe(take(1))
624
828
  .subscribe({
625
829
  next: () => {
626
- this.busy = false;
830
+ this.busy.set(false);
627
831
  this._snackbar.open($localize `:auth-jwt-admin:Registration succeeded`, 'OK');
628
832
  this._userRepository.reset();
629
833
  this.registered.emit();
630
834
  },
631
835
  error: (error) => {
632
- this.busy = false;
836
+ this.busy.set(false);
633
837
  console.error('Registration error' + (error ? JSON.stringify(error) : ''));
634
838
  this._snackbar.open($localize `:auth-jwt-admin:Registration error`, 'OK');
635
839
  },
636
840
  });
637
841
  }
638
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.0", ngImport: i0, type: AuthJwtRegistrationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
639
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.0", 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]=\"hide ? '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)=\"hide = !hide\"\r\n i18n-aria-label=\"auth-jwt-login\"\r\n aria-label=\"Hide password\"\r\n [attr.aria-pressed]=\"hide\"\r\n >\r\n <mat-icon>{{ hide ? \"visibility_off\" : \"visibility\" }}</mat-icon>\r\n </button>\r\n <auth-jwt-password-strength-bar [passwordToCheck]=\"password.value\">\r\n </auth-jwt-password-strength-bar>\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]=\"hide ? '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 silver;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:red}\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],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: PasswordStrengthBarComponent, selector: "auth-jwt-password-strength-bar", inputs: ["passwordToCheck"], outputs: ["strengthChange"] }] }); }
842
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: AuthJwtRegistrationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
843
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.0", 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 silver;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:red}\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"] }] }); }
640
844
  }
641
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImport: i0, type: AuthJwtRegistrationComponent, decorators: [{
845
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: AuthJwtRegistrationComponent, decorators: [{
642
846
  type: Component,
643
847
  args: [{ selector: 'auth-jwt-registration', imports: [
644
848
  ReactiveFormsModule,
@@ -647,9 +851,85 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImpor
647
851
  MatIconModule,
648
852
  MatInputModule,
649
853
  MatProgressSpinnerModule,
650
- PasswordStrengthBarComponent,
651
- ], 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]=\"hide ? '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)=\"hide = !hide\"\r\n i18n-aria-label=\"auth-jwt-login\"\r\n aria-label=\"Hide password\"\r\n [attr.aria-pressed]=\"hide\"\r\n >\r\n <mat-icon>{{ hide ? \"visibility_off\" : \"visibility\" }}</mat-icon>\r\n </button>\r\n <auth-jwt-password-strength-bar [passwordToCheck]=\"password.value\">\r\n </auth-jwt-password-strength-bar>\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]=\"hide ? '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 silver;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:red}\n"] }]
652
- }], ctorParameters: () => [] });
854
+ 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 silver;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:red}\n"] }]
856
+ }], ctorParameters: () => [], propDecorators: { autoConfirm: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoConfirm", required: false }] }], registered: [{ type: i0.Output, args: ["registered"] }] } });
857
+
858
+ class PasswordStrengthBarComponent {
859
+ constructor() {
860
+ this._colors = ['#F00', '#F90', '#FF0', '#9F0', '#0F0'];
861
+ this.passwordToCheck = input(...(ngDevMode ? [undefined, { debugName: "passwordToCheck" }] : []));
862
+ this.strengthChange = output();
863
+ this.bars = signal([], ...(ngDevMode ? [{ debugName: "bars" }] : []));
864
+ }
865
+ measureStrength(p) {
866
+ let force = 0;
867
+ const regex = /[$-/:-?{-~!"^_`\[\]]/g; // "
868
+ const lowerLetters = /[a-z]+/.test(p);
869
+ const upperLetters = /[A-Z]+/.test(p);
870
+ const numbers = /[0-9]+/.test(p);
871
+ const symbols = regex.test(p);
872
+ const flags = [lowerLetters, upperLetters, numbers, symbols];
873
+ let passedMatches = 0;
874
+ for (const flag of flags) {
875
+ passedMatches += flag === true ? 1 : 0;
876
+ }
877
+ force += 2 * p.length + (p.length >= 10 ? 1 : 0);
878
+ force += passedMatches * 10;
879
+ // penalty (short password)
880
+ force = p.length <= 6 ? Math.min(force, 10) : force;
881
+ // penalty (poor letiety of characters)
882
+ force = passedMatches === 1 ? Math.min(force, 10) : force;
883
+ force = passedMatches === 2 ? Math.min(force, 20) : force;
884
+ force = passedMatches === 3 ? Math.min(force, 40) : force;
885
+ return force;
886
+ }
887
+ getColor(s) {
888
+ let idx = 0;
889
+ if (s <= 10) {
890
+ idx = 0;
891
+ }
892
+ else if (s <= 20) {
893
+ idx = 1;
894
+ }
895
+ else if (s <= 30) {
896
+ idx = 2;
897
+ }
898
+ else if (s <= 40) {
899
+ idx = 3;
900
+ }
901
+ else {
902
+ idx = 4;
903
+ }
904
+ return {
905
+ idx: idx + 1,
906
+ col: this._colors[idx],
907
+ };
908
+ }
909
+ ngOnChanges(changes) {
910
+ const password = changes['passwordToCheck'].currentValue;
911
+ this.setBarColors(5, '#DDD');
912
+ if (password) {
913
+ const strength = this.measureStrength(password);
914
+ const c = this.getColor(strength);
915
+ this.setBarColors(c.idx, c.col);
916
+ this.strengthChange.emit(strength);
917
+ }
918
+ }
919
+ setBarColors(count, col) {
920
+ const bars = this.bars();
921
+ for (let n = 0; n < count; n++) {
922
+ bars[n] = col;
923
+ }
924
+ this.bars.set(bars);
925
+ }
926
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: PasswordStrengthBarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
927
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.0.0", 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:#ddd;border-radius:2px;display:inline-block;height:5px;margin-right:1px;width:20px}\n"] }); }
928
+ }
929
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: PasswordStrengthBarComponent, decorators: [{
930
+ 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:#ddd;border-radius:2px;display:inline-block;height:5px;margin-right:1px;width:20px}\n"] }]
932
+ }], propDecorators: { passwordToCheck: [{ type: i0.Input, args: [{ isSignal: true, alias: "passwordToCheck", required: false }] }], strengthChange: [{ type: i0.Output, args: ["strengthChange"] }] } });
653
933
 
654
934
  /**
655
935
  * Users filter.
@@ -659,11 +939,11 @@ class UserFilterComponent {
659
939
  /**
660
940
  * The filter.
661
941
  */
662
- this.filter = input();
942
+ this.filter = input(...(ngDevMode ? [undefined, { debugName: "filter" }] : []));
663
943
  /**
664
944
  * Whether the filter is disabled.
665
945
  */
666
- this.disabled = input();
946
+ this.disabled = input(...(ngDevMode ? [undefined, { debugName: "disabled" }] : []));
667
947
  /**
668
948
  * Event emitted when the filter changes.
669
949
  */
@@ -710,10 +990,10 @@ class UserFilterComponent {
710
990
  this._filter = this.getFilter();
711
991
  this.filterChange.emit(this._filter);
712
992
  }
713
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.0", ngImport: i0, type: UserFilterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
714
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.0.0", 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],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: i7.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }] }); }
993
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: UserFilterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
994
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.0.0", 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"] }] }); }
715
995
  }
716
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImport: i0, type: UserFilterComponent, decorators: [{
996
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: UserFilterComponent, decorators: [{
717
997
  type: Component,
718
998
  args: [{ selector: 'auth-jwt-user-filter', imports: [
719
999
  ReactiveFormsModule,
@@ -723,7 +1003,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImpor
723
1003
  MatInputModule,
724
1004
  MatTooltipModule,
725
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"] }]
726
- }], ctorParameters: () => [] });
1006
+ }], 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"] }] } });
727
1007
 
728
1008
  /**
729
1009
  * User editor form.
@@ -731,10 +1011,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImpor
731
1011
  class UserEditorComponent {
732
1012
  constructor() {
733
1013
  this._authService = inject(AuthJwtService);
1014
+ this.unlocked = signal(undefined, ...(ngDevMode ? [{ debugName: "unlocked" }] : []));
734
1015
  /**
735
1016
  * The user to edit.
736
1017
  */
737
- this.user = input(undefined);
1018
+ this.user = input(undefined, ...(ngDevMode ? [{ debugName: "user" }] : []));
738
1019
  /**
739
1020
  * Emitted when user changes.
740
1021
  */
@@ -793,9 +1074,8 @@ class UserEditorComponent {
793
1074
  else {
794
1075
  this.roles.setValue(null);
795
1076
  }
796
- this.unlocked =
797
- user.lockoutEnabled &&
798
- (!user.lockoutEnd || user.lockoutEnd < this._authService.getUTCDate());
1077
+ this.unlocked.set(user.lockoutEnabled &&
1078
+ (!user.lockoutEnd || user.lockoutEnd < this._authService.getUTCDate()));
799
1079
  }
800
1080
  }
801
1081
  getUserFromForm() {
@@ -812,7 +1092,7 @@ class UserEditorComponent {
812
1092
  };
813
1093
  }
814
1094
  endLockout() {
815
- if (this.unlocked) {
1095
+ if (this.unlocked()) {
816
1096
  return;
817
1097
  }
818
1098
  const user = this.getUserFromForm();
@@ -828,10 +1108,10 @@ class UserEditorComponent {
828
1108
  }
829
1109
  this.userChange.emit(this.getUserFromForm());
830
1110
  }
831
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.0", ngImport: i0, type: UserEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
832
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.0", 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],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: i7.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }] }); }
1111
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: UserEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1112
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.0", 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"] }] }); }
833
1113
  }
834
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImport: i0, type: UserEditorComponent, decorators: [{
1114
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: UserEditorComponent, decorators: [{
835
1115
  type: Component,
836
1116
  args: [{ selector: 'auth-jwt-user-editor', imports: [
837
1117
  ReactiveFormsModule,
@@ -841,8 +1121,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImpor
841
1121
  MatIconModule,
842
1122
  MatInputModule,
843
1123
  MatTooltipModule,
844
- ], 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"] }]
845
- }], ctorParameters: () => [] });
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"] }]
1125
+ }], 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"] }] } });
846
1126
 
847
1127
  /**
848
1128
  * List of users.
@@ -894,10 +1174,10 @@ class UserListComponent {
894
1174
  getGravatarUrl(email, options) {
895
1175
  return this._gravatarService.buildGravatarUrl(email, options);
896
1176
  }
897
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.0", ngImport: i0, type: UserListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
898
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.0", type: UserListComponent, isStandalone: true, selector: "auth-jwt-user-list", ngImport: i0, template: "<div id=\"container\">\r\n <div>\r\n <!-- filters -->\r\n <div id=\"filters\">\r\n <auth-jwt-user-filter\r\n [filter]=\"filter$ | async\"\r\n (filterChange)=\"onFilterChange($event)\"\r\n ></auth-jwt-user-filter>\r\n </div>\r\n\r\n <!-- list -->\r\n @if (page$ | async; as page) {\r\n <div id=\"list\">\r\n @if (loading$ | async) {\r\n <div>\r\n <mat-progress-bar mode=\"indeterminate\"></mat-progress-bar>\r\n </div>\r\n }\r\n <table>\r\n <thead>\r\n <td></td>\r\n <td></td>\r\n <th i18n=\"auth-jwt-admin\">name</th>\r\n <th i18n=\"auth-jwt-admin\" class=\"noif-lt-md\">first</th>\r\n <th i18n=\"auth-jwt-admin\" class=\"noif-lt-md\">last</th>\r\n <th i18n=\"auth-jwt-admin\" class=\"noif-lt-md\">email</th>\r\n <th i18n=\"auth-jwt-admin\">roles</th>\r\n <th i18n=\"auth-jwt-admin\" class=\"noif-lt-md\">lock end</th>\r\n </thead>\r\n <tbody>\r\n @for (user of page.items; track user) {\r\n <tr>\r\n <td class=\"fit-width\">\r\n <button\r\n mat-icon-button\r\n type=\"button\"\r\n i18n-matTooltip=\"auth-jwt-admin\"\r\n matTooltip=\"Edit this user\"\r\n color=\"primary\"\r\n (click)=\"setActiveUser(user)\"\r\n >\r\n <mat-icon class=\"mat-primary\">mode_edit</mat-icon>\r\n </button>\r\n <button\r\n mat-icon-button\r\n type=\"button\"\r\n i18n-matTooltip=\"auth-jwt-admin\"\r\n matTooltip=\"Delete this user\"\r\n color=\"warn\"\r\n (click)=\"deleteUser(user)\"\r\n >\r\n <mat-icon class=\"mat-warn\">remove_circle</mat-icon>\r\n </button>\r\n </td>\r\n <td class=\"fit-width\">\r\n <img\r\n alt=\"avatar\"\r\n [src]=\"getGravatarUrl(user.email, { size: '32' })\"\r\n [alt]=\"user.userName\"\r\n />\r\n </td>\r\n <td>{{ user.userName }}</td>\r\n <td class=\"noif-lt-md\">{{ user.firstName }}</td>\r\n <td class=\"noif-lt-md\">{{ user.lastName }}</td>\r\n <td class=\"noif-lt-md\">\r\n <a [href]=\"'mailto:' + user.email\">{{ user.email }}</a>\r\n </td>\r\n <td>{{ user.roles.join(\" \") }}</td>\r\n <td class=\"noif-lt-md\">{{ user.lockoutEnd }}</td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n <!-- paginator -->\r\n <div class=\"form-row\">\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n color=\"warn\"\r\n i18n-matmatTooltip=\"auth-jwt-admin\"\r\n matTooltip=\"Refresh list\"\r\n (click)=\"reset()\"\r\n >\r\n <mat-icon class=\"mat-warn\">autorenew</mat-icon>\r\n </button>\r\n <mat-paginator\r\n [length]=\"page.total\"\r\n [pageIndex]=\"page.pageNumber - 1\"\r\n [pageSize]=\"page.pageSize\"\r\n [pageSizeOptions]=\"[5, 10, 20, 50, 100]\"\r\n (page)=\"onPageChange($event)\"\r\n [showFirstLastButtons]=\"true\"\r\n ></mat-paginator>\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- editor -->\r\n <mat-expansion-panel\r\n id=\"editor\"\r\n [expanded]=\"active$ | async\"\r\n [disabled]=\"!(active$ | async)\"\r\n >\r\n @if (active$ | async; as active) {\r\n <div>\r\n <fieldset>\r\n <legend>{{ active.userName }}</legend>\r\n <auth-jwt-user-editor\r\n [user]=\"active\"\r\n (userChange)=\"saveActiveUser($event)\"\r\n (editorClose)=\"resetActiveUser()\"\r\n ></auth-jwt-user-editor>\r\n </fieldset>\r\n </div>\r\n }\r\n </mat-expansion-panel>\r\n</div>\r\n", styles: ["tr:nth-child(odd){background-color:#f8f8f8}th{padding:0 8px;text-align:left;color:silver;font-weight:400}td{padding:0 8px}td.fit-width{width:1px;white-space:nowrap}table{width:100%;border-collapse:collapse}fieldset{border:1px solid silver;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: "pipe", type: i1$1.AsyncPipe, name: "async" }, { 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: i3$2.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: i5$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: i6$1.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i7.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"] }] }); }
1177
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: UserListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1178
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.0", 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: ["tr:nth-child(odd){background-color:#f8f8f8}th{padding:0 8px;text-align:left;color:silver;font-weight:400}td{padding:0 8px}td.fit-width{width:1px;white-space:nowrap}table{width:100%;border-collapse:collapse}fieldset{border:1px solid silver;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" }] }); }
899
1179
  }
900
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImport: i0, type: UserListComponent, decorators: [{
1180
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: UserListComponent, decorators: [{
901
1181
  type: Component,
902
1182
  args: [{ selector: 'auth-jwt-user-list', imports: [
903
1183
  CommonModule,
@@ -911,7 +1191,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImpor
911
1191
  MatTooltipModule,
912
1192
  UserEditorComponent,
913
1193
  UserFilterComponent,
914
- ], template: "<div id=\"container\">\r\n <div>\r\n <!-- filters -->\r\n <div id=\"filters\">\r\n <auth-jwt-user-filter\r\n [filter]=\"filter$ | async\"\r\n (filterChange)=\"onFilterChange($event)\"\r\n ></auth-jwt-user-filter>\r\n </div>\r\n\r\n <!-- list -->\r\n @if (page$ | async; as page) {\r\n <div id=\"list\">\r\n @if (loading$ | async) {\r\n <div>\r\n <mat-progress-bar mode=\"indeterminate\"></mat-progress-bar>\r\n </div>\r\n }\r\n <table>\r\n <thead>\r\n <td></td>\r\n <td></td>\r\n <th i18n=\"auth-jwt-admin\">name</th>\r\n <th i18n=\"auth-jwt-admin\" class=\"noif-lt-md\">first</th>\r\n <th i18n=\"auth-jwt-admin\" class=\"noif-lt-md\">last</th>\r\n <th i18n=\"auth-jwt-admin\" class=\"noif-lt-md\">email</th>\r\n <th i18n=\"auth-jwt-admin\">roles</th>\r\n <th i18n=\"auth-jwt-admin\" class=\"noif-lt-md\">lock end</th>\r\n </thead>\r\n <tbody>\r\n @for (user of page.items; track user) {\r\n <tr>\r\n <td class=\"fit-width\">\r\n <button\r\n mat-icon-button\r\n type=\"button\"\r\n i18n-matTooltip=\"auth-jwt-admin\"\r\n matTooltip=\"Edit this user\"\r\n color=\"primary\"\r\n (click)=\"setActiveUser(user)\"\r\n >\r\n <mat-icon class=\"mat-primary\">mode_edit</mat-icon>\r\n </button>\r\n <button\r\n mat-icon-button\r\n type=\"button\"\r\n i18n-matTooltip=\"auth-jwt-admin\"\r\n matTooltip=\"Delete this user\"\r\n color=\"warn\"\r\n (click)=\"deleteUser(user)\"\r\n >\r\n <mat-icon class=\"mat-warn\">remove_circle</mat-icon>\r\n </button>\r\n </td>\r\n <td class=\"fit-width\">\r\n <img\r\n alt=\"avatar\"\r\n [src]=\"getGravatarUrl(user.email, { size: '32' })\"\r\n [alt]=\"user.userName\"\r\n />\r\n </td>\r\n <td>{{ user.userName }}</td>\r\n <td class=\"noif-lt-md\">{{ user.firstName }}</td>\r\n <td class=\"noif-lt-md\">{{ user.lastName }}</td>\r\n <td class=\"noif-lt-md\">\r\n <a [href]=\"'mailto:' + user.email\">{{ user.email }}</a>\r\n </td>\r\n <td>{{ user.roles.join(\" \") }}</td>\r\n <td class=\"noif-lt-md\">{{ user.lockoutEnd }}</td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n <!-- paginator -->\r\n <div class=\"form-row\">\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n color=\"warn\"\r\n i18n-matmatTooltip=\"auth-jwt-admin\"\r\n matTooltip=\"Refresh list\"\r\n (click)=\"reset()\"\r\n >\r\n <mat-icon class=\"mat-warn\">autorenew</mat-icon>\r\n </button>\r\n <mat-paginator\r\n [length]=\"page.total\"\r\n [pageIndex]=\"page.pageNumber - 1\"\r\n [pageSize]=\"page.pageSize\"\r\n [pageSizeOptions]=\"[5, 10, 20, 50, 100]\"\r\n (page)=\"onPageChange($event)\"\r\n [showFirstLastButtons]=\"true\"\r\n ></mat-paginator>\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- editor -->\r\n <mat-expansion-panel\r\n id=\"editor\"\r\n [expanded]=\"active$ | async\"\r\n [disabled]=\"!(active$ | async)\"\r\n >\r\n @if (active$ | async; as active) {\r\n <div>\r\n <fieldset>\r\n <legend>{{ active.userName }}</legend>\r\n <auth-jwt-user-editor\r\n [user]=\"active\"\r\n (userChange)=\"saveActiveUser($event)\"\r\n (editorClose)=\"resetActiveUser()\"\r\n ></auth-jwt-user-editor>\r\n </fieldset>\r\n </div>\r\n }\r\n </mat-expansion-panel>\r\n</div>\r\n", styles: ["tr:nth-child(odd){background-color:#f8f8f8}th{padding:0 8px;text-align:left;color:silver;font-weight:400}td{padding:0 8px}td.fit-width{width:1px;white-space:nowrap}table{width:100%;border-collapse:collapse}fieldset{border:1px solid silver;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"] }]
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: ["tr:nth-child(odd){background-color:#f8f8f8}th{padding:0 8px;text-align:left;color:silver;font-weight:400}td{padding:0 8px}td.fit-width{width:1px;white-space:nowrap}table{width:100%;border-collapse:collapse}fieldset{border:1px solid silver;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"] }]
915
1195
  }], ctorParameters: () => [] });
916
1196
 
917
1197
  /*
@@ -922,5 +1202,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImpor
922
1202
  * Generated bundle index. Do not edit.
923
1203
  */
924
1204
 
925
- export { AuthJwtAccountService, AuthJwtRegistrationComponent, PasswordStrengthBarComponent, PasswordValidators, UserFilterComponent, UserListComponent, UserListRepository };
1205
+ export { AuthJwtAccountService, AuthJwtRegistrationComponent, DEFAULT_RULE_LABELS, PasswordChecklistComponent, PasswordStrengthBarComponent, PasswordValidators, UserFilterComponent, UserListComponent, UserListRepository };
926
1206
  //# sourceMappingURL=myrmidon-auth-jwt-admin.mjs.map