@myrmidon/auth-jwt-admin 0.1.2 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/esm2020/lib/auth-jwt-admin.module.mjs +5 -5
  2. package/esm2020/lib/components/auth-jwt-registration/auth-jwt-registration.component.mjs +6 -6
  3. package/esm2020/lib/components/confirm-dialog/confirm-dialog.component.mjs +3 -3
  4. package/esm2020/lib/components/password-strength-bar/password-strength-bar.component.mjs +3 -3
  5. package/esm2020/lib/components/state/user-list.repository.mjs +147 -0
  6. package/esm2020/lib/components/user-editor/user-editor.component.mjs +7 -7
  7. package/esm2020/lib/components/user-filter/user-filter.component.mjs +15 -18
  8. package/esm2020/lib/components/user-list/user-list.component.mjs +34 -102
  9. package/esm2020/lib/services/auth-jwt-account.service.mjs +7 -7
  10. package/esm2020/lib/services/dialog.service.mjs +3 -3
  11. package/fesm2015/myrmidon-auth-jwt-admin.mjs +188 -221
  12. package/fesm2015/myrmidon-auth-jwt-admin.mjs.map +1 -1
  13. package/fesm2020/myrmidon-auth-jwt-admin.mjs +187 -219
  14. package/fesm2020/myrmidon-auth-jwt-admin.mjs.map +1 -1
  15. package/lib/components/auth-jwt-registration/auth-jwt-registration.component.d.ts +1 -1
  16. package/lib/components/confirm-dialog/confirm-dialog.component.d.ts +1 -1
  17. package/lib/components/password-strength-bar/password-strength-bar.component.d.ts +1 -1
  18. package/lib/components/state/user-list.repository.d.ts +36 -0
  19. package/lib/components/user-editor/user-editor.component.d.ts +1 -1
  20. package/lib/components/user-filter/user-filter.component.d.ts +7 -8
  21. package/lib/components/user-list/user-list.component.d.ts +11 -20
  22. package/lib/services/auth-jwt-account.service.d.ts +1 -3
  23. package/package.json +6 -6
  24. package/esm2020/lib/components/state/users.paginator.mjs +0 -12
  25. package/esm2020/lib/components/state/users.query.mjs +0 -20
  26. package/esm2020/lib/components/state/users.service.mjs +0 -65
  27. package/esm2020/lib/components/state/users.store.mjs +0 -27
  28. package/lib/components/state/users.paginator.d.ts +0 -3
  29. package/lib/components/state/users.query.d.ts +0 -12
  30. package/lib/components/state/users.service.d.ts +0 -19
  31. package/lib/components/state/users.store.d.ts +0 -13
@@ -1,40 +1,41 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Injectable, EventEmitter, Component, Input, Output, InjectionToken, inject, Optional, Inject, NgModule } from '@angular/core';
2
+ import { Injectable, EventEmitter, Component, Input, Output, Optional, Inject, NgModule } from '@angular/core';
3
3
  import * as i1$1 from '@angular/forms';
4
4
  import { Validators, FormsModule, ReactiveFormsModule } from '@angular/forms';
5
- import { retry, catchError, take, startWith, tap, switchMap, map } from 'rxjs/operators';
5
+ import { retry, catchError, take } from 'rxjs/operators';
6
6
  import * as i2$1 from '@angular/material/snack-bar';
7
7
  import { MatSnackBarModule } from '@angular/material/snack-bar';
8
8
  import * as i1 from '@angular/common/http';
9
9
  import { HttpParams, HttpClientModule } from '@angular/common/http';
10
10
  import * as i2 from '@myrmidon/ng-tools';
11
11
  import { NgToolsModule } from '@myrmidon/ng-tools';
12
- import * as i7 from '@angular/common';
12
+ import * as i4 from '@angular/common';
13
13
  import { CommonModule } from '@angular/common';
14
- import * as i4 from '@angular/material/button';
14
+ import * as i5 from '@angular/material/button';
15
15
  import { MatButtonModule } from '@angular/material/button';
16
16
  import * as i6 from '@angular/material/icon';
17
17
  import { MatIconModule } from '@angular/material/icon';
18
- import * as i7$1 from '@angular/material/form-field';
19
- import * as i8 from '@angular/material/input';
18
+ import * as i7 from '@angular/material/input';
20
19
  import { MatInputModule } from '@angular/material/input';
20
+ import * as i8 from '@angular/material/form-field';
21
21
  import * as i9 from '@angular/material/progress-spinner';
22
22
  import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
23
- import * as i15 from '@datorama/akita';
24
- import { EntityStore, StoreConfig, QueryEntity, PaginatorPlugin } from '@datorama/akita';
25
- import { __decorate } from 'tslib';
26
- import * as i8$1 from '@angular/material/tooltip';
23
+ import { BehaviorSubject, combineLatest, map, debounceTime, take as take$1 } from 'rxjs';
24
+ import { select, createStore, withProps } from '@ngneat/elf';
25
+ import { selectActiveEntity, withEntities, withActiveId, upsertEntities, deleteAllEntities, setActiveId, updateEntities } from '@ngneat/elf-entities';
26
+ import { selectPaginationData, selectCurrentPageEntities, deleteAllPages, withPagination, updatePaginationData, setPage, hasPage, setCurrentPage } from '@ngneat/elf-pagination';
27
+ import { selectRequestStatus, withRequestsCache, withRequestsStatus, updateRequestStatus } from '@ngneat/elf-requests';
28
+ import * as i9$1 from '@angular/material/tooltip';
27
29
  import { MatTooltipModule } from '@angular/material/tooltip';
28
- import { BehaviorSubject, combineLatest } from 'rxjs';
29
30
  import * as i1$2 from '@angular/material/dialog';
30
31
  import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
31
32
  import * as i2$2 from '@myrmidon/auth-jwt-login';
32
33
  import { AuthJwtLoginModule } from '@myrmidon/auth-jwt-login';
33
- import * as i10 from '@angular/material/paginator';
34
+ import * as i7$1 from '@angular/material/paginator';
34
35
  import { MatPaginatorModule } from '@angular/material/paginator';
35
- import * as i11 from '@angular/material/progress-bar';
36
+ import * as i8$1 from '@angular/material/progress-bar';
36
37
  import { MatProgressBarModule } from '@angular/material/progress-bar';
37
- import * as i5 from '@angular/material/checkbox';
38
+ import * as i5$1 from '@angular/material/checkbox';
38
39
  import { MatCheckboxModule } from '@angular/material/checkbox';
39
40
  import { MatCardModule } from '@angular/material/card';
40
41
  import { RouterModule } from '@angular/router';
@@ -148,10 +149,10 @@ class AuthJwtAccountService {
148
149
  })
149
150
  .pipe(retry(3), catchError(this._error.handleError));
150
151
  }
151
- getUsers(filter) {
152
+ getUsers(filter, pageNumber = 1, pageSize = 20) {
152
153
  let httpParams = new HttpParams();
153
- httpParams = httpParams.set('pageNumber', filter.pageNumber.toString());
154
- httpParams = httpParams.set('pageSize', filter.pageSize.toString());
154
+ httpParams = httpParams.set('pageNumber', pageNumber.toString());
155
+ httpParams = httpParams.set('pageSize', pageSize.toString());
155
156
  if (filter.name) {
156
157
  httpParams = httpParams.set('name', filter.name);
157
158
  }
@@ -248,9 +249,9 @@ class AuthJwtAccountService {
248
249
  .pipe(catchError(this._error.handleError));
249
250
  }
250
251
  }
251
- AuthJwtAccountService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.6", ngImport: i0, type: AuthJwtAccountService, deps: [{ token: i1.HttpClient }, { token: i2.ErrorService }, { token: i2.EnvService }], target: i0.ɵɵFactoryTarget.Injectable });
252
- AuthJwtAccountService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.2.6", ngImport: i0, type: AuthJwtAccountService, providedIn: 'root' });
253
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.6", ngImport: i0, type: AuthJwtAccountService, decorators: [{
252
+ AuthJwtAccountService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: AuthJwtAccountService, deps: [{ token: i1.HttpClient }, { token: i2.ErrorService }, { token: i2.EnvService }], target: i0.ɵɵFactoryTarget.Injectable });
253
+ AuthJwtAccountService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: AuthJwtAccountService, providedIn: 'root' });
254
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: AuthJwtAccountService, decorators: [{
254
255
  type: Injectable,
255
256
  args: [{
256
257
  providedIn: 'root',
@@ -324,9 +325,9 @@ class PasswordStrengthBarComponent {
324
325
  }
325
326
  }
326
327
  }
327
- PasswordStrengthBarComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.6", ngImport: i0, type: PasswordStrengthBarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
328
- PasswordStrengthBarComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.2.6", type: PasswordStrengthBarComponent, selector: "auth-jwt-password-strength-bar", inputs: { passwordToCheck: "passwordToCheck" }, 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"] });
329
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.6", ngImport: i0, type: PasswordStrengthBarComponent, decorators: [{
328
+ PasswordStrengthBarComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: PasswordStrengthBarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
329
+ PasswordStrengthBarComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.0.0", type: PasswordStrengthBarComponent, selector: "auth-jwt-password-strength-bar", inputs: { passwordToCheck: "passwordToCheck" }, 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"] });
330
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: PasswordStrengthBarComponent, decorators: [{
330
331
  type: Component,
331
332
  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"] }]
332
333
  }], ctorParameters: function () { return []; }, propDecorators: { passwordToCheck: [{
@@ -514,118 +515,158 @@ class AuthJwtRegistrationComponent {
514
515
  });
515
516
  }
516
517
  }
517
- AuthJwtRegistrationComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.6", ngImport: i0, type: AuthJwtRegistrationComponent, deps: [{ token: i1$1.UntypedFormBuilder }, { token: i2$1.MatSnackBar }, { token: AuthJwtAccountService }], target: i0.ɵɵFactoryTarget.Component });
518
- AuthJwtRegistrationComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.2.6", type: AuthJwtRegistrationComponent, selector: "auth-jwt-registration", outputs: { registered: "registered" }, ngImport: i0, template: "<div>\n <div>\n <p>\n To register a new user, you must provide his email address, choose a\n password, and choose a username, which must be unique (you will be\n notified if another user has already taken that name). The username must\n include only letters/digits, start with a letter, and be no shorter than 3\n characters, nor longer than 50.\n </p>\n <p>\n To promote a decent security level, the password must include at least 8\n characters, uppercase and lowercase letters, digits, and punctuation (like\n dashes, stops, parentheses, etc.).\n </p>\n <p>\n Once registered, the user will receive an email message to the email\n address you specified; he will have to click on the provided link to\n complete the registration process.\n </p>\n </div>\n\n <form role=\"form\" [formGroup]=\"registration\" (submit)=\"onSubmit()\">\n <fieldset>\n <label>register user</label>\n <!-- email -->\n <div>\n <mat-form-field>\n <input\n matInput\n type=\"email\"\n id=\"email\"\n maxlength=\"256\"\n required\n autofocus\n spellcheck=\"false\"\n placeholder=\"email\"\n formControlName=\"email\"\n />\n <mat-error>{{ getEmailErrorLabel() }}</mat-error>\n <mat-icon *ngIf=\"email.pending\">hourglass</mat-icon>\n </mat-form-field>\n </div>\n\n <!-- name -->\n <div>\n <mat-form-field>\n <input\n matInput\n type=\"text\"\n id=\"name\"\n maxlength=\"50\"\n required\n pattern=\"^[a-zA-Z][a-zA-Z0-9]{2,49}$\"\n spellcheck=\"false\"\n placeholder=\"username\"\n formControlName=\"name\"\n />\n <mat-error>{{ getNameErrorLabel() }}</mat-error>\n <mat-icon *ngIf=\"name.pending\">hourglass</mat-icon>\n </mat-form-field>\n </div>\n\n <!-- first name -->\n <div>\n <mat-form-field>\n <input\n matInput\n type=\"text\"\n id=\"firstName\"\n maxlength=\"50\"\n required\n spellcheck=\"false\"\n placeholder=\"first name\"\n formControlName=\"firstName\"\n />\n <mat-error\n *ngIf=\"\n firstName.hasError('required') &&\n (firstName.dirty || firstName.touched)\n \"\n class=\"text-danger small\"\n >\n first name required\n </mat-error>\n </mat-form-field>\n </div>\n\n <!-- last name -->\n <div>\n <mat-form-field>\n <input\n matInput\n type=\"text\"\n id=\"lastName\"\n maxlength=\"50\"\n required\n spellcheck=\"false\"\n placeholder=\"last name\"\n formControlName=\"lastName\"\n />\n <mat-error\n *ngIf=\"\n lastName.hasError('required') &&\n (lastName.dirty || lastName.touched)\n \"\n class=\"text-danger small\"\n >\n last name required\n </mat-error>\n </mat-form-field>\n </div>\n\n <div [formGroup]=\"passwords\">\n <!-- password -->\n <div>\n <mat-form-field>\n <input\n matInput\n type=\"password\"\n name=\"password\"\n autocomplete=\"new-password\"\n maxlength=\"50\"\n required\n spellcheck=\"false\"\n placeholder=\"password\"\n formControlName=\"password\"\n />\n <auth-jwt-password-strength-bar [passwordToCheck]=\"password.value\">\n </auth-jwt-password-strength-bar>\n <mat-error>{{ getPasswordErrorLabel() }}</mat-error>\n </mat-form-field>\n </div>\n\n <!-- confirm password -->\n <div>\n <mat-form-field>\n <input\n matInput\n type=\"password\"\n name=\"confirmPassword\"\n maxlength=\"50\"\n required\n spellcheck=\"false\"\n placeholder=\"confirm password\"\n formControlName=\"confirmPassword\"\n />\n <mat-error\n *ngIf=\"\n passwords.hasError('areEqual') &&\n (confirmPassword.dirty || confirmPassword.touched)\n \"\n >\n password differs from confirmation password\n </mat-error>\n </mat-form-field>\n </div>\n </div>\n\n <button\n mat-raised-button\n type=\"submit\"\n color=\"primary\"\n [disabled]=\"\n !registration.valid || busy || name.pending || email.pending\n \"\n >\n register\n </button>\n <mat-progress-spinner\n diameter=\"20\"\n *ngIf=\"busy\"\n aria-label=\"Busy\"\n ></mat-progress-spinner>\n </fieldset>\n </form>\n</div>\n", styles: ["mat-form-field{width:400px}fieldset{border:1px solid silver;border-radius:8px;padding:16px}\n"], dependencies: [{ kind: "directive", type: i7.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.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$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$1.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i1$1.PatternValidator, selector: "[pattern][formControlName],[pattern][formControl],[pattern][ngModel]", inputs: ["pattern"] }, { kind: "directive", type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: i4.MatButton, selector: "button[mat-button], button[mat-raised-button], button[mat-icon-button], button[mat-fab], button[mat-mini-fab], button[mat-stroked-button], button[mat-flat-button]", inputs: ["disabled", "disableRipple", "color"], exportAs: ["matButton"] }, { kind: "component", type: i6.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: i7$1.MatError, selector: "mat-error", inputs: ["id"] }, { kind: "component", type: i7$1.MatFormField, selector: "mat-form-field", inputs: ["color", "appearance", "hideRequiredMarker", "hintLabel", "floatLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i8.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "component", type: i9.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "diameter", "strokeWidth", "mode", "value"], exportAs: ["matProgressSpinner"] }, { kind: "component", type: PasswordStrengthBarComponent, selector: "auth-jwt-password-strength-bar", inputs: ["passwordToCheck"], outputs: ["strengthChange"] }] });
519
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.6", ngImport: i0, type: AuthJwtRegistrationComponent, decorators: [{
518
+ AuthJwtRegistrationComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: AuthJwtRegistrationComponent, deps: [{ token: i1$1.UntypedFormBuilder }, { token: i2$1.MatSnackBar }, { token: AuthJwtAccountService }], target: i0.ɵɵFactoryTarget.Component });
519
+ AuthJwtRegistrationComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.0.0", type: AuthJwtRegistrationComponent, selector: "auth-jwt-registration", outputs: { registered: "registered" }, ngImport: i0, template: "<div>\n <div>\n <p>\n To register a new user, you must provide his email address, choose a\n password, and choose a username, which must be unique (you will be\n notified if another user has already taken that name). The username must\n include only letters/digits, start with a letter, and be no shorter than 3\n characters, nor longer than 50.\n </p>\n <p>\n To promote a decent security level, the password must include at least 8\n characters, uppercase and lowercase letters, digits, and punctuation (like\n dashes, stops, parentheses, etc.).\n </p>\n <p>\n Once registered, the user will receive an email message to the email\n address you specified; he will have to click on the provided link to\n complete the registration process.\n </p>\n </div>\n\n <form role=\"form\" [formGroup]=\"registration\" (submit)=\"onSubmit()\">\n <fieldset>\n <label>register user</label>\n <!-- email -->\n <div>\n <mat-form-field>\n <input\n matInput\n type=\"email\"\n id=\"email\"\n maxlength=\"256\"\n required\n autofocus\n spellcheck=\"false\"\n placeholder=\"email\"\n formControlName=\"email\"\n />\n <mat-error>{{ getEmailErrorLabel() }}</mat-error>\n <mat-icon *ngIf=\"email.pending\">hourglass</mat-icon>\n </mat-form-field>\n </div>\n\n <!-- name -->\n <div>\n <mat-form-field>\n <input\n matInput\n type=\"text\"\n id=\"name\"\n maxlength=\"50\"\n required\n pattern=\"^[a-zA-Z][a-zA-Z0-9]{2,49}$\"\n spellcheck=\"false\"\n placeholder=\"username\"\n formControlName=\"name\"\n />\n <mat-error>{{ getNameErrorLabel() }}</mat-error>\n <mat-icon *ngIf=\"name.pending\">hourglass</mat-icon>\n </mat-form-field>\n </div>\n\n <!-- first name -->\n <div>\n <mat-form-field>\n <input\n matInput\n type=\"text\"\n id=\"firstName\"\n maxlength=\"50\"\n required\n spellcheck=\"false\"\n placeholder=\"first name\"\n formControlName=\"firstName\"\n />\n <mat-error\n *ngIf=\"\n firstName.hasError('required') &&\n (firstName.dirty || firstName.touched)\n \"\n class=\"text-danger small\"\n >\n first name required\n </mat-error>\n </mat-form-field>\n </div>\n\n <!-- last name -->\n <div>\n <mat-form-field>\n <input\n matInput\n type=\"text\"\n id=\"lastName\"\n maxlength=\"50\"\n required\n spellcheck=\"false\"\n placeholder=\"last name\"\n formControlName=\"lastName\"\n />\n <mat-error\n *ngIf=\"\n lastName.hasError('required') &&\n (lastName.dirty || lastName.touched)\n \"\n class=\"text-danger small\"\n >\n last name required\n </mat-error>\n </mat-form-field>\n </div>\n\n <div [formGroup]=\"passwords\">\n <!-- password -->\n <div>\n <mat-form-field>\n <input\n matInput\n type=\"password\"\n name=\"password\"\n autocomplete=\"new-password\"\n maxlength=\"50\"\n required\n spellcheck=\"false\"\n placeholder=\"password\"\n formControlName=\"password\"\n />\n <auth-jwt-password-strength-bar [passwordToCheck]=\"password.value\">\n </auth-jwt-password-strength-bar>\n <mat-error>{{ getPasswordErrorLabel() }}</mat-error>\n </mat-form-field>\n </div>\n\n <!-- confirm password -->\n <div>\n <mat-form-field>\n <input\n matInput\n type=\"password\"\n name=\"confirmPassword\"\n maxlength=\"50\"\n required\n spellcheck=\"false\"\n placeholder=\"confirm password\"\n formControlName=\"confirmPassword\"\n />\n <mat-error\n *ngIf=\"\n passwords.hasError('areEqual') &&\n (confirmPassword.dirty || confirmPassword.touched)\n \"\n >\n password differs from confirmation password\n </mat-error>\n </mat-form-field>\n </div>\n </div>\n\n <button\n mat-raised-button\n type=\"submit\"\n color=\"primary\"\n [disabled]=\"\n !registration.valid || busy || name.pending || email.pending\n \"\n >\n register\n </button>\n <mat-progress-spinner\n diameter=\"20\"\n *ngIf=\"busy\"\n aria-label=\"Busy\"\n ></mat-progress-spinner>\n </fieldset>\n </form>\n</div>\n", styles: ["mat-form-field{width:400px}fieldset{border:1px solid silver;border-radius:8px;padding:16px}\n"], dependencies: [{ kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.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$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$1.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i1$1.PatternValidator, selector: "[pattern][formControlName],[pattern][formControl],[pattern][ngModel]", inputs: ["pattern"] }, { kind: "directive", type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: i5.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", inputs: ["disabled", "disableRipple", "color"], exportAs: ["matButton"] }, { kind: "component", type: i6.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: i7.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "component", type: i8.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i8.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "component", type: i9.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"] }] });
520
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: AuthJwtRegistrationComponent, decorators: [{
520
521
  type: Component,
521
522
  args: [{ selector: 'auth-jwt-registration', template: "<div>\n <div>\n <p>\n To register a new user, you must provide his email address, choose a\n password, and choose a username, which must be unique (you will be\n notified if another user has already taken that name). The username must\n include only letters/digits, start with a letter, and be no shorter than 3\n characters, nor longer than 50.\n </p>\n <p>\n To promote a decent security level, the password must include at least 8\n characters, uppercase and lowercase letters, digits, and punctuation (like\n dashes, stops, parentheses, etc.).\n </p>\n <p>\n Once registered, the user will receive an email message to the email\n address you specified; he will have to click on the provided link to\n complete the registration process.\n </p>\n </div>\n\n <form role=\"form\" [formGroup]=\"registration\" (submit)=\"onSubmit()\">\n <fieldset>\n <label>register user</label>\n <!-- email -->\n <div>\n <mat-form-field>\n <input\n matInput\n type=\"email\"\n id=\"email\"\n maxlength=\"256\"\n required\n autofocus\n spellcheck=\"false\"\n placeholder=\"email\"\n formControlName=\"email\"\n />\n <mat-error>{{ getEmailErrorLabel() }}</mat-error>\n <mat-icon *ngIf=\"email.pending\">hourglass</mat-icon>\n </mat-form-field>\n </div>\n\n <!-- name -->\n <div>\n <mat-form-field>\n <input\n matInput\n type=\"text\"\n id=\"name\"\n maxlength=\"50\"\n required\n pattern=\"^[a-zA-Z][a-zA-Z0-9]{2,49}$\"\n spellcheck=\"false\"\n placeholder=\"username\"\n formControlName=\"name\"\n />\n <mat-error>{{ getNameErrorLabel() }}</mat-error>\n <mat-icon *ngIf=\"name.pending\">hourglass</mat-icon>\n </mat-form-field>\n </div>\n\n <!-- first name -->\n <div>\n <mat-form-field>\n <input\n matInput\n type=\"text\"\n id=\"firstName\"\n maxlength=\"50\"\n required\n spellcheck=\"false\"\n placeholder=\"first name\"\n formControlName=\"firstName\"\n />\n <mat-error\n *ngIf=\"\n firstName.hasError('required') &&\n (firstName.dirty || firstName.touched)\n \"\n class=\"text-danger small\"\n >\n first name required\n </mat-error>\n </mat-form-field>\n </div>\n\n <!-- last name -->\n <div>\n <mat-form-field>\n <input\n matInput\n type=\"text\"\n id=\"lastName\"\n maxlength=\"50\"\n required\n spellcheck=\"false\"\n placeholder=\"last name\"\n formControlName=\"lastName\"\n />\n <mat-error\n *ngIf=\"\n lastName.hasError('required') &&\n (lastName.dirty || lastName.touched)\n \"\n class=\"text-danger small\"\n >\n last name required\n </mat-error>\n </mat-form-field>\n </div>\n\n <div [formGroup]=\"passwords\">\n <!-- password -->\n <div>\n <mat-form-field>\n <input\n matInput\n type=\"password\"\n name=\"password\"\n autocomplete=\"new-password\"\n maxlength=\"50\"\n required\n spellcheck=\"false\"\n placeholder=\"password\"\n formControlName=\"password\"\n />\n <auth-jwt-password-strength-bar [passwordToCheck]=\"password.value\">\n </auth-jwt-password-strength-bar>\n <mat-error>{{ getPasswordErrorLabel() }}</mat-error>\n </mat-form-field>\n </div>\n\n <!-- confirm password -->\n <div>\n <mat-form-field>\n <input\n matInput\n type=\"password\"\n name=\"confirmPassword\"\n maxlength=\"50\"\n required\n spellcheck=\"false\"\n placeholder=\"confirm password\"\n formControlName=\"confirmPassword\"\n />\n <mat-error\n *ngIf=\"\n passwords.hasError('areEqual') &&\n (confirmPassword.dirty || confirmPassword.touched)\n \"\n >\n password differs from confirmation password\n </mat-error>\n </mat-form-field>\n </div>\n </div>\n\n <button\n mat-raised-button\n type=\"submit\"\n color=\"primary\"\n [disabled]=\"\n !registration.valid || busy || name.pending || email.pending\n \"\n >\n register\n </button>\n <mat-progress-spinner\n diameter=\"20\"\n *ngIf=\"busy\"\n aria-label=\"Busy\"\n ></mat-progress-spinner>\n </fieldset>\n </form>\n</div>\n", styles: ["mat-form-field{width:400px}fieldset{border:1px solid silver;border-radius:8px;padding:16px}\n"] }]
522
523
  }], ctorParameters: function () { return [{ type: i1$1.UntypedFormBuilder }, { type: i2$1.MatSnackBar }, { type: AuthJwtAccountService }]; }, propDecorators: { registered: [{
523
524
  type: Output
524
525
  }] } });
525
526
 
526
- const initialState = {
527
- filter: {
528
- pageNumber: 1,
529
- pageSize: 20,
530
- },
531
- active: null
532
- };
533
- let UsersStore = class UsersStore extends EntityStore {
534
- constructor() {
535
- super(initialState);
536
- }
537
- };
538
- UsersStore.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.6", ngImport: i0, type: UsersStore, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
539
- UsersStore.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.2.6", ngImport: i0, type: UsersStore, providedIn: 'root' });
540
- UsersStore = __decorate([
541
- StoreConfig({ name: 'Users', idKey: 'userName', resettable: true })
542
- ], UsersStore);
543
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.6", ngImport: i0, type: UsersStore, decorators: [{
544
- type: Injectable,
545
- args: [{ providedIn: 'root' }]
546
- }], ctorParameters: function () { return []; } });
547
-
548
- class UsersQuery extends QueryEntity {
549
- constructor(_store) {
550
- super(_store);
551
- this._store = _store;
527
+ const PAGE_SIZE = 20;
528
+ class UserListRepository {
529
+ constructor(_accService) {
530
+ this._accService = _accService;
531
+ // create store
532
+ this._store = this.createStore();
533
+ this._lastPageSize = PAGE_SIZE;
534
+ this._loading$ = new BehaviorSubject(false);
535
+ this._saving$ = new BehaviorSubject(false);
536
+ this.loading$ = this._loading$.asObservable();
537
+ this.saving$ = this._saving$.asObservable();
538
+ // combine pagination parameters with page data for our consumers
539
+ this.pagination$ = combineLatest([
540
+ this._store.pipe(selectPaginationData()),
541
+ this._store.pipe(selectCurrentPageEntities()),
542
+ ]).pipe(map(([pagination, data]) => ({ ...pagination, data })), debounceTime(0));
543
+ // the active document, if required
544
+ this.activeUser$ = this._store.pipe(selectActiveEntity());
545
+ // the filter, if required
546
+ this.filter$ = this._store.pipe(select((state) => state.filter));
547
+ this.filter$.subscribe((filter) => {
548
+ // when filter changed, reset any existing page and move to page 1
549
+ const paginationData = this._store.getValue().pagination;
550
+ console.log('Deleting all pages');
551
+ this._store.update(deleteAllPages());
552
+ // load page 1
553
+ this.loadPage(1, paginationData.perPage);
554
+ });
555
+ // the request status
556
+ this.status$ = this._store.pipe(selectRequestStatus('document'));
557
+ // load page 1 and subscribe to pagination
558
+ this.loadPage(1, PAGE_SIZE);
559
+ this.pagination$.subscribe(console.log);
560
+ }
561
+ createStore() {
562
+ const store = createStore({ name: 'user-list' }, withProps({
563
+ filter: {},
564
+ }), withEntities({ idKey: 'userName' }), withActiveId(), withRequestsCache(), withRequestsStatus(), withPagination());
565
+ return store;
566
+ }
567
+ adaptPage(page) {
568
+ // adapt the server page DataPage<T> to Elf pagination
569
+ return {
570
+ currentPage: page.pageNumber,
571
+ perPage: page.pageSize,
572
+ lastPage: page.pageCount,
573
+ total: page.total,
574
+ data: page.items,
575
+ };
552
576
  }
553
- selectFilter() {
554
- return this.select((state) => state.filter);
577
+ addPage(response) {
578
+ const { data, ...paginationData } = response;
579
+ this._store.update(upsertEntities(data),
580
+ // addEntities(data),
581
+ updatePaginationData(paginationData), setPage(paginationData.currentPage, data.map((c) => c.userName)));
582
+ }
583
+ loadPage(pageNumber, pageSize) {
584
+ if (!pageSize) {
585
+ pageSize = PAGE_SIZE;
586
+ }
587
+ // if the page exists and page size is the same, just move to it
588
+ if (this._store.query(hasPage(pageNumber)) &&
589
+ pageSize === this._lastPageSize) {
590
+ console.log('Page exists: ' + pageNumber);
591
+ this._store.update(setCurrentPage(pageNumber));
592
+ return;
593
+ }
594
+ // reset cached pages if page size changed
595
+ if (this._lastPageSize !== pageSize) {
596
+ this._store.update(deleteAllPages());
597
+ this._lastPageSize = pageSize;
598
+ }
599
+ // load page from server
600
+ this._store.update(updateRequestStatus('document', 'pending'));
601
+ this._loading$.next(true);
602
+ this._accService
603
+ .getUsers(this._store.getValue().filter, pageNumber, pageSize)
604
+ .pipe(take$1(1))
605
+ .subscribe((page) => {
606
+ this._loading$.next(false);
607
+ this.addPage({ ...this.adaptPage(page), data: page.items });
608
+ this._store.update(updateRequestStatus('document', 'success'));
609
+ });
555
610
  }
556
- }
557
- UsersQuery.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.6", ngImport: i0, type: UsersQuery, deps: [{ token: UsersStore }], target: i0.ɵɵFactoryTarget.Injectable });
558
- UsersQuery.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.2.6", ngImport: i0, type: UsersQuery, providedIn: 'root' });
559
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.6", ngImport: i0, type: UsersQuery, decorators: [{
560
- type: Injectable,
561
- args: [{ providedIn: 'root' }]
562
- }], ctorParameters: function () { return [{ type: UsersStore }]; } });
563
-
564
- class UsersService {
565
- constructor(_store, _accountService) {
566
- this._store = _store;
567
- this._accountService = _accountService;
611
+ setFilter(filter) {
612
+ this._store.update((state) => ({ ...state, filter: filter }));
568
613
  }
569
- /**
570
- * Update the filter in the store.
571
- * @param filter The filter.
572
- */
573
- updateFilter(filter) {
574
- this._store.update({
575
- filter: filter,
576
- });
614
+ clearCache() {
615
+ this._store.update(deleteAllEntities(), deleteAllPages());
577
616
  }
578
617
  setActive(name) {
579
- this._store.setActive(name);
618
+ this._store.update(setActiveId(name));
580
619
  }
581
620
  updateActive(user) {
582
621
  const promise = new Promise((resolve, reject) => {
583
- this._store.setLoading(true);
584
- this._accountService.updateUser(user).subscribe((_) => {
585
- this._store.setLoading(false);
586
- this._store.setError(null);
587
- this._store.updateActive((u) => {
588
- return { ...user };
589
- });
590
- resolve(true);
591
- }, (error) => {
592
- console.error(error);
593
- this._store.setLoading(false);
594
- this._store.setError('Error updating user');
595
- resolve(false);
622
+ this._saving$.next(true);
623
+ this._accService.updateUser(user).subscribe({
624
+ next: (_) => {
625
+ this._saving$.next(false);
626
+ this._store.update(updateEntities(user.userName, user));
627
+ resolve(true);
628
+ },
629
+ error: (error) => {
630
+ this._saving$.next(false);
631
+ console.error(`Error updating user ${user.userName}: ` +
632
+ JSON.stringify(error || {}));
633
+ resolve(false);
634
+ },
596
635
  });
597
636
  });
598
637
  return promise;
599
638
  }
600
639
  deleteUser(name) {
601
640
  const promise = new Promise((resolve, reject) => {
602
- this._store.setLoading(true);
603
- this._accountService.deleteUser(name).subscribe((_) => {
604
- this._store.setLoading(false);
605
- this._store.setError(null);
606
- this._store.remove(name);
607
- resolve(true);
608
- }, (error) => {
609
- console.error(error);
610
- this._store.setLoading(false);
611
- this._store.setError('Error deleting user');
612
- reject(error);
641
+ this._saving$.next(true);
642
+ this._accService.deleteUser(name).subscribe({
643
+ next: (_) => {
644
+ this._saving$.next(false);
645
+ this.clearCache();
646
+ this.loadPage(1);
647
+ resolve(true);
648
+ },
649
+ error: (error) => {
650
+ this._saving$.next(false);
651
+ console.error(`Error deleting user ${name}: ` + JSON.stringify(error || {}));
652
+ reject(error);
653
+ },
613
654
  });
614
655
  });
615
656
  return promise;
616
657
  }
617
658
  }
618
- UsersService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.6", ngImport: i0, type: UsersService, deps: [{ token: UsersStore }, { token: AuthJwtAccountService }], target: i0.ɵɵFactoryTarget.Injectable });
619
- UsersService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.2.6", ngImport: i0, type: UsersService, providedIn: 'root' });
620
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.6", ngImport: i0, type: UsersService, decorators: [{
659
+ UserListRepository.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: UserListRepository, deps: [{ token: AuthJwtAccountService }], target: i0.ɵɵFactoryTarget.Injectable });
660
+ UserListRepository.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: UserListRepository, providedIn: 'root' });
661
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: UserListRepository, decorators: [{
621
662
  type: Injectable,
622
663
  args: [{ providedIn: 'root' }]
623
- }], ctorParameters: function () { return [{ type: UsersStore }, { type: AuthJwtAccountService }]; } });
664
+ }], ctorParameters: function () { return [{ type: AuthJwtAccountService }]; } });
624
665
 
625
666
  class UserFilterComponent {
626
- constructor(formBuilder, query, _corporaService) {
627
- this._corporaService = _corporaService;
628
- this.filter$ = query.selectFilter();
667
+ constructor(formBuilder, _repository) {
668
+ this._repository = _repository;
669
+ this.filter$ = _repository.filter$;
629
670
  // form
630
671
  this.name = formBuilder.control(null);
631
672
  this.form = formBuilder.group({
@@ -638,7 +679,7 @@ class UserFilterComponent {
638
679
  });
639
680
  }
640
681
  updateForm(filter) {
641
- this.name.setValue(filter.name);
682
+ this.name.setValue(filter.name || null);
642
683
  this.form.markAsPristine();
643
684
  }
644
685
  reset() {
@@ -647,8 +688,6 @@ class UserFilterComponent {
647
688
  }
648
689
  getFilter() {
649
690
  return {
650
- pageNumber: 1,
651
- pageSize: 20,
652
691
  name: this.name.value?.trim(),
653
692
  };
654
693
  }
@@ -658,27 +697,18 @@ class UserFilterComponent {
658
697
  }
659
698
  const filter = this.getFilter();
660
699
  // update filter in state
661
- this._corporaService.updateFilter(filter);
700
+ this._repository.setFilter(filter);
662
701
  }
663
702
  }
664
- UserFilterComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.6", ngImport: i0, type: UserFilterComponent, deps: [{ token: i1$1.UntypedFormBuilder }, { token: UsersQuery }, { token: UsersService }], target: i0.ɵɵFactoryTarget.Component });
665
- UserFilterComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.2.6", type: UserFilterComponent, selector: "auth-jwt-user-filter", inputs: { disabled: "disabled" }, ngImport: i0, template: "<form [formGroup]=\"form\" (submit)=\"apply()\" ng-disabled=\"disabled\">\n <mat-form-field>\n <input matInput [formControl]=\"name\" placeholder=\"name or ID\" />\n <button\n mat-icon-button\n matSuffix\n type=\"button\"\n (click)=\"reset()\"\n color=\"warn\"\n matTooltip=\"Reset filters\"\n [disabled]=\"disabled\"\n >\n <mat-icon>clear</mat-icon>\n </button>\n </mat-form-field>\n\n <button\n type=\"submit\"\n mat-icon-button\n color=\"primary\"\n [disabled]=\"disabled\"\n matTooltip=\"Apply filters\"\n >\n <mat-icon>check_circle</mat-icon>\n </button>\n</form>\n", styles: [""], dependencies: [{ kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.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$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: i4.MatButton, selector: "button[mat-button], button[mat-raised-button], button[mat-icon-button], button[mat-fab], button[mat-mini-fab], button[mat-stroked-button], button[mat-flat-button]", inputs: ["disabled", "disableRipple", "color"], exportAs: ["matButton"] }, { kind: "component", type: i6.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i7$1.MatFormField, selector: "mat-form-field", inputs: ["color", "appearance", "hideRequiredMarker", "hintLabel", "floatLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i7$1.MatSuffix, selector: "[matSuffix]" }, { kind: "directive", type: i8.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "directive", type: i8$1.MatTooltip, selector: "[matTooltip]", exportAs: ["matTooltip"] }] });
666
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.6", ngImport: i0, type: UserFilterComponent, decorators: [{
703
+ UserFilterComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: UserFilterComponent, deps: [{ token: i1$1.FormBuilder }, { token: UserListRepository }], target: i0.ɵɵFactoryTarget.Component });
704
+ UserFilterComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.0.0", type: UserFilterComponent, selector: "auth-jwt-user-filter", inputs: { disabled: "disabled" }, ngImport: i0, template: "<form [formGroup]=\"form\" (submit)=\"apply()\" ng-disabled=\"disabled\">\n <mat-form-field>\n <input matInput [formControl]=\"name\" placeholder=\"name or ID\" />\n <button\n mat-icon-button\n matSuffix\n type=\"button\"\n (click)=\"reset()\"\n color=\"warn\"\n matTooltip=\"Reset filters\"\n [disabled]=\"disabled\"\n >\n <mat-icon>clear</mat-icon>\n </button>\n </mat-form-field>\n\n <button\n type=\"submit\"\n mat-icon-button\n color=\"primary\"\n [disabled]=\"disabled\"\n matTooltip=\"Apply filters\"\n >\n <mat-icon>check_circle</mat-icon>\n </button>\n</form>\n", styles: [""], dependencies: [{ kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.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$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: i5.MatIconButton, selector: "button[mat-icon-button]", inputs: ["disabled", "disableRipple", "color"], exportAs: ["matButton"] }, { kind: "component", type: i6.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: i7.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "component", type: i8.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i8.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]" }, { kind: "directive", type: i9$1.MatTooltip, selector: "[matTooltip]", exportAs: ["matTooltip"] }] });
705
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: UserFilterComponent, decorators: [{
667
706
  type: Component,
668
707
  args: [{ selector: 'auth-jwt-user-filter', template: "<form [formGroup]=\"form\" (submit)=\"apply()\" ng-disabled=\"disabled\">\n <mat-form-field>\n <input matInput [formControl]=\"name\" placeholder=\"name or ID\" />\n <button\n mat-icon-button\n matSuffix\n type=\"button\"\n (click)=\"reset()\"\n color=\"warn\"\n matTooltip=\"Reset filters\"\n [disabled]=\"disabled\"\n >\n <mat-icon>clear</mat-icon>\n </button>\n </mat-form-field>\n\n <button\n type=\"submit\"\n mat-icon-button\n color=\"primary\"\n [disabled]=\"disabled\"\n matTooltip=\"Apply filters\"\n >\n <mat-icon>check_circle</mat-icon>\n </button>\n</form>\n" }]
669
- }], ctorParameters: function () { return [{ type: i1$1.UntypedFormBuilder }, { type: UsersQuery }, { type: UsersService }]; }, propDecorators: { disabled: [{
708
+ }], ctorParameters: function () { return [{ type: i1$1.FormBuilder }, { type: UserListRepository }]; }, propDecorators: { disabled: [{
670
709
  type: Input
671
710
  }] } });
672
711
 
673
- // create a factory provider for the paginator
674
- const USERS_PAGINATOR = new InjectionToken('USERS_PAGINATOR', {
675
- providedIn: 'root',
676
- factory: () => {
677
- const query = inject(UsersQuery);
678
- return new PaginatorPlugin(query).withControls().withRange();
679
- },
680
- });
681
-
682
712
  // https://medium.com/@tarik.nzl/making-use-of-dialogs-in-material-2-mddialog-7533d27df41
683
713
  class ConfirmDialogComponent {
684
714
  constructor(dialogRef, data) {
@@ -691,9 +721,9 @@ class ConfirmDialogComponent {
691
721
  }
692
722
  ngOnInit() { }
693
723
  }
694
- ConfirmDialogComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.6", ngImport: i0, type: ConfirmDialogComponent, deps: [{ token: i1$2.MatDialogRef }, { token: MAT_DIALOG_DATA, optional: true }], target: i0.ɵɵFactoryTarget.Component });
695
- ConfirmDialogComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.2.6", type: ConfirmDialogComponent, selector: "ng-component", ngImport: i0, template: "<h1 mat-dialog-title>{{ title }}</h1>\r\n<mat-dialog-content>\r\n {{ prompt }}\r\n</mat-dialog-content>\r\n<mat-dialog-actions>\r\n <button\r\n type=\"button\"\r\n mat-raised-button\r\n color=\"warn\"\r\n (click)=\"dialogRef.close(true)\"\r\n >\r\n {{ ok }}\r\n </button>\r\n <button type=\"button\" mat-button (click)=\"dialogRef.close()\">\r\n {{ cancel }}\r\n </button>\r\n</mat-dialog-actions>\r\n", styles: [""], dependencies: [{ kind: "component", type: i4.MatButton, selector: "button[mat-button], button[mat-raised-button], button[mat-icon-button], button[mat-fab], button[mat-mini-fab], button[mat-stroked-button], button[mat-flat-button]", inputs: ["disabled", "disableRipple", "color"], exportAs: ["matButton"] }, { kind: "directive", type: i1$2.MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: i1$2.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "directive", type: i1$2.MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }] });
696
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.6", ngImport: i0, type: ConfirmDialogComponent, decorators: [{
724
+ ConfirmDialogComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: ConfirmDialogComponent, deps: [{ token: i1$2.MatDialogRef }, { token: MAT_DIALOG_DATA, optional: true }], target: i0.ɵɵFactoryTarget.Component });
725
+ ConfirmDialogComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.0.0", type: ConfirmDialogComponent, selector: "ng-component", ngImport: i0, template: "<h1 mat-dialog-title>{{ title }}</h1>\r\n<mat-dialog-content>\r\n {{ prompt }}\r\n</mat-dialog-content>\r\n<mat-dialog-actions>\r\n <button\r\n type=\"button\"\r\n mat-raised-button\r\n color=\"warn\"\r\n (click)=\"dialogRef.close(true)\"\r\n >\r\n {{ ok }}\r\n </button>\r\n <button type=\"button\" mat-button (click)=\"dialogRef.close()\">\r\n {{ cancel }}\r\n </button>\r\n</mat-dialog-actions>\r\n", styles: [""], dependencies: [{ kind: "component", type: i5.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", inputs: ["disabled", "disableRipple", "color"], exportAs: ["matButton"] }, { kind: "directive", type: i1$2.MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: i1$2.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "directive", type: i1$2.MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }] });
726
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: ConfirmDialogComponent, decorators: [{
697
727
  type: Component,
698
728
  args: [{ template: "<h1 mat-dialog-title>{{ title }}</h1>\r\n<mat-dialog-content>\r\n {{ prompt }}\r\n</mat-dialog-content>\r\n<mat-dialog-actions>\r\n <button\r\n type=\"button\"\r\n mat-raised-button\r\n color=\"warn\"\r\n (click)=\"dialogRef.close(true)\"\r\n >\r\n {{ ok }}\r\n </button>\r\n <button type=\"button\" mat-button (click)=\"dialogRef.close()\">\r\n {{ cancel }}\r\n </button>\r\n</mat-dialog-actions>\r\n" }]
699
729
  }], ctorParameters: function () { return [{ type: i1$2.MatDialogRef }, { type: undefined, decorators: [{
@@ -727,9 +757,9 @@ class DialogService {
727
757
  return dialogRef.afterClosed();
728
758
  }
729
759
  }
730
- DialogService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.6", ngImport: i0, type: DialogService, deps: [{ token: i1$2.MatDialog }], target: i0.ɵɵFactoryTarget.Injectable });
731
- DialogService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.2.6", ngImport: i0, type: DialogService, providedIn: 'root' });
732
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.6", ngImport: i0, type: DialogService, decorators: [{
760
+ DialogService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: DialogService, deps: [{ token: i1$2.MatDialog }], target: i0.ɵɵFactoryTarget.Injectable });
761
+ DialogService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: DialogService, providedIn: 'root' });
762
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: DialogService, decorators: [{
733
763
  type: Injectable,
734
764
  args: [{
735
765
  providedIn: 'root'
@@ -823,11 +853,11 @@ class UserEditorComponent {
823
853
  this.userChange.emit(this.getUserFromForm());
824
854
  }
825
855
  }
826
- UserEditorComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.6", ngImport: i0, type: UserEditorComponent, deps: [{ token: i1$1.UntypedFormBuilder }, { token: i2$2.AuthJwtService }], target: i0.ɵɵFactoryTarget.Component });
827
- UserEditorComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.2.6", type: UserEditorComponent, selector: "auth-jwt-user-editor", inputs: { user: "user" }, outputs: { userChange: "userChange", editorClose: "editorClose" }, ngImport: i0, template: "<form [formGroup]=\"form\" (submit)=\"save()\">\n <!-- email -->\n <div>\n <mat-form-field>\n <input type=\"text\" matInput [formControl]=\"email\" placeholder=\"email\" />\n <mat-error\n *ngIf=\"email.hasError('required') && (email.dirty || email.touched)\"\n >\n email address required\n </mat-error>\n <mat-error\n *ngIf=\"email.hasError('pattern') && (email.dirty || email.touched)\"\n >\n invalid email address\n </mat-error>\n </mat-form-field>\n </div>\n\n <!-- emailConfirmed -->\n <div>\n <mat-checkbox [formControl]=\"emailConfirmed\"\n >email address confirmed</mat-checkbox\n >\n </div>\n\n <!-- lockoutEnabled -->\n <div>\n <mat-checkbox [formControl]=\"lockoutEnabled\"\n >lockout enabled</mat-checkbox\n >\n &nbsp;\n <button\n mat-stroked-button\n (click)=\"endLockout()\"\n [disabled]=\"unlocked\"\n matTooltip=\"Unlock this user if locked\"\n >\n unlock\n </button>\n </div>\n\n <!-- firstName -->\n <div>\n <mat-form-field>\n <input\n type=\"text\"\n matInput\n [formControl]=\"firstName\"\n placeholder=\"first name\"\n />\n <mat-error\n *ngIf=\"\n firstName.hasError('required') &&\n (firstName.dirty || firstName.touched)\n \"\n >\n first name required\n </mat-error>\n <mat-error\n *ngIf=\"\n firstName.hasError('maxlength') &&\n (firstName.dirty || firstName.touched)\n \"\n >\n first name too long\n </mat-error>\n </mat-form-field>\n </div>\n\n <!-- lastName -->\n <div>\n <mat-form-field>\n <input\n type=\"text\"\n matInput\n [formControl]=\"lastName\"\n placeholder=\"last name\"\n />\n <mat-error\n *ngIf=\"\n lastName.hasError('required') && (lastName.dirty || lastName.touched)\n \"\n >\n last name required\n </mat-error>\n <mat-error\n *ngIf=\"\n lastName.hasError('maxlength') && (lastName.dirty || lastName.touched)\n \"\n >\n last name too long\n </mat-error>\n </mat-form-field>\n </div>\n\n <!-- roles -->\n <div>\n <mat-form-field>\n <input\n type=\"text\"\n matInput\n [formControl]=\"roles\"\n placeholder=\"roles (separated by space)\"\n />\n <mat-error\n *ngIf=\"roles.hasError('maxlength') && (roles.dirty || roles.touched)\"\n >\n too long\n </mat-error>\n </mat-form-field>\n </div>\n\n <!-- buttons -->\n <br />\n <div>\n <button mat-icon-button color=\"warn\" matTooltip=\"Close\" (click)=\"close()\">\n <mat-icon>cancel</mat-icon>\n </button>\n &nbsp;\n <button\n type=\"submit\"\n mat-button\n color=\"primary\"\n [disabled]=\"form.invalid\"\n matTooltip=\"Save user data\"\n >\n <mat-icon>check_circle</mat-icon> save\n </button>\n </div>\n</form>\n", styles: [""], dependencies: [{ kind: "directive", type: i7.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.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$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: i4.MatButton, selector: "button[mat-button], button[mat-raised-button], button[mat-icon-button], button[mat-fab], button[mat-mini-fab], button[mat-stroked-button], button[mat-flat-button]", inputs: ["disabled", "disableRipple", "color"], exportAs: ["matButton"] }, { kind: "component", type: i5.MatCheckbox, selector: "mat-checkbox", inputs: ["disableRipple", "color", "tabIndex"], exportAs: ["matCheckbox"] }, { kind: "component", type: i6.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: i7$1.MatError, selector: "mat-error", inputs: ["id"] }, { kind: "component", type: i7$1.MatFormField, selector: "mat-form-field", inputs: ["color", "appearance", "hideRequiredMarker", "hintLabel", "floatLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i8.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "directive", type: i8$1.MatTooltip, selector: "[matTooltip]", exportAs: ["matTooltip"] }] });
828
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.6", ngImport: i0, type: UserEditorComponent, decorators: [{
856
+ UserEditorComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: UserEditorComponent, deps: [{ token: i1$1.UntypedFormBuilder }, { token: i2$2.AuthJwtService }], target: i0.ɵɵFactoryTarget.Component });
857
+ UserEditorComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.0.0", type: UserEditorComponent, selector: "auth-jwt-user-editor", inputs: { user: "user" }, outputs: { userChange: "userChange", editorClose: "editorClose" }, ngImport: i0, template: "<form [formGroup]=\"form\" (submit)=\"save()\">\n <!-- email -->\n <div>\n <mat-form-field>\n <mat-label>email</mat-label>\n <input type=\"text\" matInput [formControl]=\"email\" />\n <mat-error\n *ngIf=\"email.hasError('required') && (email.dirty || email.touched)\"\n >\n email address required\n </mat-error>\n <mat-error\n *ngIf=\"email.hasError('pattern') && (email.dirty || email.touched)\"\n >\n invalid email address\n </mat-error>\n </mat-form-field>\n </div>\n\n <!-- emailConfirmed -->\n <div>\n <mat-checkbox [formControl]=\"emailConfirmed\"\n >email address confirmed</mat-checkbox\n >\n </div>\n\n <!-- lockoutEnabled -->\n <div>\n <mat-checkbox [formControl]=\"lockoutEnabled\">lockout enabled</mat-checkbox>\n &nbsp;\n <button\n mat-icon-button\n color=\"primary\"\n (click)=\"endLockout()\"\n [disabled]=\"unlocked\"\n matTooltip=\"Unlock this user if locked\"\n >\n <mat-icon>lock_open</mat-icon>\n </button>\n </div>\n\n <!-- firstName -->\n <div>\n <mat-form-field>\n <mat-label>first name</mat-label>\n <input type=\"text\" matInput [formControl]=\"firstName\" />\n <mat-error\n *ngIf=\"\n firstName.hasError('required') &&\n (firstName.dirty || firstName.touched)\n \"\n >\n first name required\n </mat-error>\n <mat-error\n *ngIf=\"\n firstName.hasError('maxlength') &&\n (firstName.dirty || firstName.touched)\n \"\n >\n first name too long\n </mat-error>\n </mat-form-field>\n </div>\n\n <!-- lastName -->\n <div>\n <mat-form-field>\n <mat-label>last name</mat-label>\n <input type=\"text\" matInput [formControl]=\"lastName\" />\n <mat-error\n *ngIf=\"\n lastName.hasError('required') && (lastName.dirty || lastName.touched)\n \"\n >\n last name required\n </mat-error>\n <mat-error\n *ngIf=\"\n lastName.hasError('maxlength') && (lastName.dirty || lastName.touched)\n \"\n >\n last name too long\n </mat-error>\n </mat-form-field>\n </div>\n\n <!-- roles -->\n <div>\n <mat-form-field>\n <mat-label>roles</mat-label>\n <input type=\"text\" matInput [formControl]=\"roles\" />\n <mat-error\n *ngIf=\"roles.hasError('maxlength') && (roles.dirty || roles.touched)\"\n >\n too long\n </mat-error>\n <mat-hint>roles (separated by space)</mat-hint>\n </mat-form-field>\n </div>\n\n <!-- buttons -->\n <br />\n <div>\n <button mat-icon-button color=\"warn\" matTooltip=\"Close\" (click)=\"close()\">\n <mat-icon>cancel</mat-icon>\n </button>\n &nbsp;\n <button\n type=\"submit\"\n mat-icon-button\n color=\"primary\"\n [disabled]=\"form.invalid\"\n matTooltip=\"Save user data\"\n >\n <mat-icon>check_circle</mat-icon>\n </button>\n </div>\n</form>\n", styles: [""], dependencies: [{ kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.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$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: i5.MatIconButton, selector: "button[mat-icon-button]", inputs: ["disabled", "disableRipple", "color"], exportAs: ["matButton"] }, { kind: "component", type: i5$1.MatCheckbox, selector: "mat-checkbox", inputs: ["disableRipple", "color", "tabIndex"], exportAs: ["matCheckbox"] }, { kind: "component", type: i6.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: i7.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "component", type: i8.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i8.MatLabel, selector: "mat-label" }, { kind: "directive", type: i8.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: i8.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i9$1.MatTooltip, selector: "[matTooltip]", exportAs: ["matTooltip"] }] });
858
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: UserEditorComponent, decorators: [{
829
859
  type: Component,
830
- args: [{ selector: 'auth-jwt-user-editor', template: "<form [formGroup]=\"form\" (submit)=\"save()\">\n <!-- email -->\n <div>\n <mat-form-field>\n <input type=\"text\" matInput [formControl]=\"email\" placeholder=\"email\" />\n <mat-error\n *ngIf=\"email.hasError('required') && (email.dirty || email.touched)\"\n >\n email address required\n </mat-error>\n <mat-error\n *ngIf=\"email.hasError('pattern') && (email.dirty || email.touched)\"\n >\n invalid email address\n </mat-error>\n </mat-form-field>\n </div>\n\n <!-- emailConfirmed -->\n <div>\n <mat-checkbox [formControl]=\"emailConfirmed\"\n >email address confirmed</mat-checkbox\n >\n </div>\n\n <!-- lockoutEnabled -->\n <div>\n <mat-checkbox [formControl]=\"lockoutEnabled\"\n >lockout enabled</mat-checkbox\n >\n &nbsp;\n <button\n mat-stroked-button\n (click)=\"endLockout()\"\n [disabled]=\"unlocked\"\n matTooltip=\"Unlock this user if locked\"\n >\n unlock\n </button>\n </div>\n\n <!-- firstName -->\n <div>\n <mat-form-field>\n <input\n type=\"text\"\n matInput\n [formControl]=\"firstName\"\n placeholder=\"first name\"\n />\n <mat-error\n *ngIf=\"\n firstName.hasError('required') &&\n (firstName.dirty || firstName.touched)\n \"\n >\n first name required\n </mat-error>\n <mat-error\n *ngIf=\"\n firstName.hasError('maxlength') &&\n (firstName.dirty || firstName.touched)\n \"\n >\n first name too long\n </mat-error>\n </mat-form-field>\n </div>\n\n <!-- lastName -->\n <div>\n <mat-form-field>\n <input\n type=\"text\"\n matInput\n [formControl]=\"lastName\"\n placeholder=\"last name\"\n />\n <mat-error\n *ngIf=\"\n lastName.hasError('required') && (lastName.dirty || lastName.touched)\n \"\n >\n last name required\n </mat-error>\n <mat-error\n *ngIf=\"\n lastName.hasError('maxlength') && (lastName.dirty || lastName.touched)\n \"\n >\n last name too long\n </mat-error>\n </mat-form-field>\n </div>\n\n <!-- roles -->\n <div>\n <mat-form-field>\n <input\n type=\"text\"\n matInput\n [formControl]=\"roles\"\n placeholder=\"roles (separated by space)\"\n />\n <mat-error\n *ngIf=\"roles.hasError('maxlength') && (roles.dirty || roles.touched)\"\n >\n too long\n </mat-error>\n </mat-form-field>\n </div>\n\n <!-- buttons -->\n <br />\n <div>\n <button mat-icon-button color=\"warn\" matTooltip=\"Close\" (click)=\"close()\">\n <mat-icon>cancel</mat-icon>\n </button>\n &nbsp;\n <button\n type=\"submit\"\n mat-button\n color=\"primary\"\n [disabled]=\"form.invalid\"\n matTooltip=\"Save user data\"\n >\n <mat-icon>check_circle</mat-icon> save\n </button>\n </div>\n</form>\n" }]
860
+ args: [{ selector: 'auth-jwt-user-editor', template: "<form [formGroup]=\"form\" (submit)=\"save()\">\n <!-- email -->\n <div>\n <mat-form-field>\n <mat-label>email</mat-label>\n <input type=\"text\" matInput [formControl]=\"email\" />\n <mat-error\n *ngIf=\"email.hasError('required') && (email.dirty || email.touched)\"\n >\n email address required\n </mat-error>\n <mat-error\n *ngIf=\"email.hasError('pattern') && (email.dirty || email.touched)\"\n >\n invalid email address\n </mat-error>\n </mat-form-field>\n </div>\n\n <!-- emailConfirmed -->\n <div>\n <mat-checkbox [formControl]=\"emailConfirmed\"\n >email address confirmed</mat-checkbox\n >\n </div>\n\n <!-- lockoutEnabled -->\n <div>\n <mat-checkbox [formControl]=\"lockoutEnabled\">lockout enabled</mat-checkbox>\n &nbsp;\n <button\n mat-icon-button\n color=\"primary\"\n (click)=\"endLockout()\"\n [disabled]=\"unlocked\"\n matTooltip=\"Unlock this user if locked\"\n >\n <mat-icon>lock_open</mat-icon>\n </button>\n </div>\n\n <!-- firstName -->\n <div>\n <mat-form-field>\n <mat-label>first name</mat-label>\n <input type=\"text\" matInput [formControl]=\"firstName\" />\n <mat-error\n *ngIf=\"\n firstName.hasError('required') &&\n (firstName.dirty || firstName.touched)\n \"\n >\n first name required\n </mat-error>\n <mat-error\n *ngIf=\"\n firstName.hasError('maxlength') &&\n (firstName.dirty || firstName.touched)\n \"\n >\n first name too long\n </mat-error>\n </mat-form-field>\n </div>\n\n <!-- lastName -->\n <div>\n <mat-form-field>\n <mat-label>last name</mat-label>\n <input type=\"text\" matInput [formControl]=\"lastName\" />\n <mat-error\n *ngIf=\"\n lastName.hasError('required') && (lastName.dirty || lastName.touched)\n \"\n >\n last name required\n </mat-error>\n <mat-error\n *ngIf=\"\n lastName.hasError('maxlength') && (lastName.dirty || lastName.touched)\n \"\n >\n last name too long\n </mat-error>\n </mat-form-field>\n </div>\n\n <!-- roles -->\n <div>\n <mat-form-field>\n <mat-label>roles</mat-label>\n <input type=\"text\" matInput [formControl]=\"roles\" />\n <mat-error\n *ngIf=\"roles.hasError('maxlength') && (roles.dirty || roles.touched)\"\n >\n too long\n </mat-error>\n <mat-hint>roles (separated by space)</mat-hint>\n </mat-form-field>\n </div>\n\n <!-- buttons -->\n <br />\n <div>\n <button mat-icon-button color=\"warn\" matTooltip=\"Close\" (click)=\"close()\">\n <mat-icon>cancel</mat-icon>\n </button>\n &nbsp;\n <button\n type=\"submit\"\n mat-icon-button\n color=\"primary\"\n [disabled]=\"form.invalid\"\n matTooltip=\"Save user data\"\n >\n <mat-icon>check_circle</mat-icon>\n </button>\n </div>\n</form>\n" }]
831
861
  }], ctorParameters: function () { return [{ type: i1$1.UntypedFormBuilder }, { type: i2$2.AuthJwtService }]; }, propDecorators: { user: [{
832
862
  type: Input
833
863
  }], userChange: [{
@@ -837,77 +867,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.6", ngImpor
837
867
  }] } });
838
868
 
839
869
  class UserListComponent {
840
- constructor(paginator, userQuery, _accountService, _usersService, _dialogService, _gravatarService, formBuilder) {
841
- this.paginator = paginator;
842
- this._accountService = _accountService;
843
- this._usersService = _usersService;
870
+ constructor(_repository, _dialogService, _gravatarService) {
871
+ this._repository = _repository;
844
872
  this._dialogService = _dialogService;
845
873
  this._gravatarService = _gravatarService;
846
- this.pageSize = formBuilder.control(20);
847
- this._refresh$ = new BehaviorSubject(0);
848
- this._filter$ = userQuery.selectFilter();
849
- // https://netbasal.com/manage-your-entities-with-akita-like-a-boss-768732f8d4d1
850
- this.active$ = userQuery.selectActive();
851
- this.pagination$ = combineLatest([
852
- this.paginator.pageChanges,
853
- this.pageSize.valueChanges.pipe(
854
- // we are required to emit at least the initial value
855
- // as combineLatest emits only if ALL observables have emitted
856
- startWith(20),
857
- // clear the cache when page size changes
858
- tap((_) => {
859
- this.paginator.clearCache();
860
- })),
861
- this._filter$.pipe(
862
- // clear the cache when filters changed
863
- tap((_) => {
864
- this.paginator.clearCache();
865
- })),
866
- this._refresh$.pipe(
867
- // clear the cache when forcing refresh
868
- tap((_) => {
869
- this.paginator.clearCache();
870
- })),
871
- ]).pipe(
872
- // for each emitted value, combine into a filter and use it
873
- // to request the page from server
874
- switchMap(([pageNumber, pageSize, filter, refresh]) => {
875
- // const filter = { ...this._docsQuery.getValue().filter };
876
- const f = { ...filter };
877
- f.pageNumber = pageNumber;
878
- f.pageSize = pageSize;
879
- const request = this.getRequest(f);
880
- // update saved filters
881
- this.paginator.metadata.set('filter', f);
882
- return this.paginator.getPage(request);
883
- }));
884
- }
885
- ngOnDestroy() {
886
- this.paginator.destroy();
887
- }
888
- getRequest(filter) {
889
- return () => this._accountService.getUsers(filter).pipe(
890
- // adapt server results to the paginator plugin
891
- map((p) => {
892
- return {
893
- currentPage: p.pageNumber,
894
- perPage: p.pageSize,
895
- lastPage: p.pageCount,
896
- data: p.items,
897
- total: p.total,
898
- };
899
- }));
874
+ this.pagination$ = _repository.pagination$;
875
+ this.active$ = _repository.activeUser$;
876
+ this.loading$ = _repository.loading$;
900
877
  }
901
878
  pageChange(event) {
902
- // https://material.angular.io/components/paginator/api
903
- this.paginator.setPage(event.pageIndex + 1);
904
- if (event.pageSize !== this.pageSize.value) {
905
- this.pageSize.setValue(event.pageSize);
906
- }
907
- }
908
- refresh() {
909
- this.paginator.clearCache();
910
- this._refresh$.next(this._refresh$.value + 1);
879
+ this._repository.loadPage(event.pageIndex + 1, event.pageSize);
911
880
  }
912
881
  deleteUser(user) {
913
882
  this._dialogService
@@ -917,21 +886,20 @@ class UserListComponent {
917
886
  if (!yes) {
918
887
  return;
919
888
  }
920
- this._usersService.deleteUser(user.userName).finally(() => {
921
- this.refresh();
889
+ this._repository.deleteUser(user.userName).finally(() => {
890
+ this._repository.clearCache();
891
+ this._repository.loadPage(1);
922
892
  });
923
893
  });
924
894
  }
925
895
  setActiveUser(user) {
926
- this._usersService.setActive(user?.userName || null);
896
+ this._repository.setActive(user?.userName || null);
927
897
  }
928
898
  resetActiveUser() {
929
- this._usersService.setActive(null);
899
+ this._repository.setActive(null);
930
900
  }
931
901
  saveActiveUser(user) {
932
- this._usersService.updateActive(user).finally(() => {
933
- this.refresh();
934
- });
902
+ this._repository.updateActive(user);
935
903
  }
936
904
  onUserEditorClose() {
937
905
  this.setActiveUser(null);
@@ -939,21 +907,21 @@ class UserListComponent {
939
907
  getGravatarUrl(email, size = 80) {
940
908
  return this._gravatarService.buildGravatarUrl(email, size);
941
909
  }
910
+ clearCache() {
911
+ this._repository.clearCache();
912
+ }
942
913
  }
943
- UserListComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.6", ngImport: i0, type: UserListComponent, deps: [{ token: USERS_PAGINATOR }, { token: UsersQuery }, { token: AuthJwtAccountService }, { token: UsersService }, { token: DialogService }, { token: i2$2.GravatarService }, { token: i1$1.UntypedFormBuilder }], target: i0.ɵɵFactoryTarget.Component });
944
- UserListComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.2.6", type: UserListComponent, selector: "auth-jwt-user-list", ngImport: i0, template: "<div\r\n gdAreas=\"filters | progress | list | pager | editor\"\r\n gdRows=\"80px 64px 2fr 40px 1fr\"\r\n gdColumns=\"1fr\"\r\n gdGap=\"8px\"\r\n>\r\n <div>\r\n <!-- filters -->\r\n <div gdArea=\"filters\">\r\n <auth-jwt-user-filter></auth-jwt-user-filter>\r\n </div>\r\n\r\n <!-- progress -->\r\n <div *ngIf=\"paginator.isLoading$ | async\" gdArea=\"progress\">\r\n <mat-progress-bar mode=\"indeterminate\"></mat-progress-bar>\r\n </div>\r\n\r\n <!-- list -->\r\n <div gdArea=\"list\" *ngIf=\"pagination$ | async as pagination\">\r\n <table>\r\n <thead>\r\n <td></td>\r\n <td></td>\r\n <td></td>\r\n <th>name</th>\r\n <th>first</th>\r\n <th>last</th>\r\n <th>email</th>\r\n <th>roles</th>\r\n <th>lock end</th>\r\n </thead>\r\n <tbody>\r\n <tr *ngFor=\"let user of pagination.data\">\r\n <td class=\"command\">\r\n <button\r\n mat-icon-button\r\n type=\"button\"\r\n matTooltip=\"Edit {{ user.userName }}\"\r\n color=\"primary\"\r\n (click)=\"setActiveUser(user)\"\r\n >\r\n <mat-icon>mode_edit</mat-icon>\r\n </button>\r\n </td>\r\n <td class=\"command\">\r\n <button\r\n mat-icon-button\r\n type=\"button\"\r\n matTooltip=\"Delete {{ user.userName }}\"\r\n color=\"warn\"\r\n (click)=\"deleteUser(user)\"\r\n >\r\n <mat-icon>remove_circle</mat-icon>\r\n </button>\r\n </td>\r\n <td>\r\n <img\r\n [src]=\"getGravatarUrl(user.email, 32)\"\r\n [alt]=\"user.userName\"\r\n />\r\n </td>\r\n <td>{{ user.userName }}</td>\r\n <td>{{ user.firstName }}</td>\r\n <td>{{ user.lastName }}</td>\r\n <td>\r\n <a [href]=\"'mailto:' + user.email\">{{ user.email }}</a>\r\n </td>\r\n <td>{{ user.roles?.join(\" \") }}</td>\r\n <td>{{ user.lockoutEnd }}</td>\r\n </tr>\r\n </tbody>\r\n </table>\r\n <!-- pagination -->\r\n <mat-paginator\r\n gdArea=\"pager\"\r\n gdAlignColumns=\"center\"\r\n gdAlignRows=\"start\"\r\n [length]=\"pagination.total\"\r\n [pageSize]=\"pageSize.value\"\r\n [pageSizeOptions]=\"[20, 50, 75, 100]\"\r\n [pageIndex]=\"pagination.currentPage - 1\"\r\n [showFirstLastButtons]=\"true\"\r\n (page)=\"pageChange($event)\"\r\n ></mat-paginator>\r\n </div>\r\n </div>\r\n\r\n <!-- editor -->\r\n <div gdArea=\"editor\" *ngIf=\"active$ | async as active\">\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</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.command{width:24px}table{width:100%;border-collapse:collapse}fieldset{border:1px solid silver;border-radius:8px;padding:16px}\n"], dependencies: [{ kind: "directive", type: i7.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i7.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i4.MatButton, selector: "button[mat-button], button[mat-raised-button], button[mat-icon-button], button[mat-fab], button[mat-mini-fab], button[mat-stroked-button], button[mat-flat-button]", inputs: ["disabled", "disableRipple", "color"], exportAs: ["matButton"] }, { kind: "component", type: i6.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i10.MatPaginator, selector: "mat-paginator", inputs: ["disabled"], exportAs: ["matPaginator"] }, { kind: "component", type: i11.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { kind: "directive", type: i8$1.MatTooltip, selector: "[matTooltip]", exportAs: ["matTooltip"] }, { kind: "component", type: UserFilterComponent, selector: "auth-jwt-user-filter", inputs: ["disabled"] }, { kind: "component", type: UserEditorComponent, selector: "auth-jwt-user-editor", inputs: ["user"], outputs: ["userChange", "editorClose"] }, { kind: "pipe", type: i7.AsyncPipe, name: "async" }] });
945
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.6", ngImport: i0, type: UserListComponent, decorators: [{
914
+ UserListComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: UserListComponent, deps: [{ token: UserListRepository }, { token: DialogService }, { token: i2$2.GravatarService }], target: i0.ɵɵFactoryTarget.Component });
915
+ UserListComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.0.0", type: UserListComponent, selector: "auth-jwt-user-list", ngImport: i0, template: "<div\n gdAreas=\"filters | progress | list | pager | editor\"\n gdRows=\"80px 64px 2fr 40px 1fr\"\n gdColumns=\"1fr\"\n gdGap=\"8px\"\n>\n <div>\n <!-- filters -->\n <div gdArea=\"filters\">\n <auth-jwt-user-filter></auth-jwt-user-filter>\n </div>\n\n <!-- progress -->\n <div *ngIf=\"loading$ | async\" gdArea=\"progress\">\n <mat-progress-bar mode=\"indeterminate\"></mat-progress-bar>\n </div>\n\n <!-- list -->\n <div gdArea=\"list\" *ngIf=\"pagination$ | async as pagination\">\n <table>\n <thead>\n <td></td>\n <td></td>\n <td></td>\n <th>name</th>\n <th>first</th>\n <th>last</th>\n <th>email</th>\n <th>roles</th>\n <th>lock end</th>\n </thead>\n <tbody>\n <tr *ngFor=\"let user of pagination.data\">\n <td class=\"command\">\n <button\n mat-icon-button\n type=\"button\"\n matTooltip=\"Edit {{ user.userName }}\"\n color=\"primary\"\n (click)=\"setActiveUser(user)\"\n >\n <mat-icon>mode_edit</mat-icon>\n </button>\n </td>\n <td class=\"command\">\n <button\n mat-icon-button\n type=\"button\"\n matTooltip=\"Delete {{ user.userName }}\"\n color=\"warn\"\n (click)=\"deleteUser(user)\"\n >\n <mat-icon>remove_circle</mat-icon>\n </button>\n </td>\n <td>\n <img\n [src]=\"getGravatarUrl(user.email, 32)\"\n [alt]=\"user.userName\"\n />\n </td>\n <td>{{ user.userName }}</td>\n <td>{{ user.firstName }}</td>\n <td>{{ user.lastName }}</td>\n <td>\n <a [href]=\"'mailto:' + user.email\">{{ user.email }}</a>\n </td>\n <td>{{ user.roles.join(\" \") }}</td>\n <td>{{ user.lockoutEnd }}</td>\n </tr>\n </tbody>\n </table>\n <!-- pagination -->\n <mat-paginator\n gdArea=\"pager\"\n gdAlignColumns=\"center\"\n gdAlignRows=\"start\"\n [length]=\"pagination.total\"\n [pageSize]=\"pagination.perPage\"\n [pageSizeOptions]=\"[20, 50, 75, 100]\"\n [pageIndex]=\"pagination.currentPage - 1\"\n [showFirstLastButtons]=\"true\"\n (page)=\"pageChange($event)\"\n ></mat-paginator>\n </div>\n </div>\n\n <!-- editor -->\n <div gdArea=\"editor\" *ngIf=\"active$ | async as active\">\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</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.command{width:24px}table{width:100%;border-collapse:collapse}fieldset{border:1px solid silver;border-radius:8px;padding:16px}\n"], dependencies: [{ kind: "directive", type: i4.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i5.MatIconButton, selector: "button[mat-icon-button]", inputs: ["disabled", "disableRipple", "color"], exportAs: ["matButton"] }, { kind: "component", type: i6.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i7$1.MatPaginator, selector: "mat-paginator", inputs: ["disabled"], exportAs: ["matPaginator"] }, { kind: "component", type: i8$1.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { kind: "directive", type: i9$1.MatTooltip, selector: "[matTooltip]", exportAs: ["matTooltip"] }, { kind: "component", type: UserFilterComponent, selector: "auth-jwt-user-filter", inputs: ["disabled"] }, { kind: "component", type: UserEditorComponent, selector: "auth-jwt-user-editor", inputs: ["user"], outputs: ["userChange", "editorClose"] }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }] });
916
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: UserListComponent, decorators: [{
946
917
  type: Component,
947
- args: [{ selector: 'auth-jwt-user-list', template: "<div\r\n gdAreas=\"filters | progress | list | pager | editor\"\r\n gdRows=\"80px 64px 2fr 40px 1fr\"\r\n gdColumns=\"1fr\"\r\n gdGap=\"8px\"\r\n>\r\n <div>\r\n <!-- filters -->\r\n <div gdArea=\"filters\">\r\n <auth-jwt-user-filter></auth-jwt-user-filter>\r\n </div>\r\n\r\n <!-- progress -->\r\n <div *ngIf=\"paginator.isLoading$ | async\" gdArea=\"progress\">\r\n <mat-progress-bar mode=\"indeterminate\"></mat-progress-bar>\r\n </div>\r\n\r\n <!-- list -->\r\n <div gdArea=\"list\" *ngIf=\"pagination$ | async as pagination\">\r\n <table>\r\n <thead>\r\n <td></td>\r\n <td></td>\r\n <td></td>\r\n <th>name</th>\r\n <th>first</th>\r\n <th>last</th>\r\n <th>email</th>\r\n <th>roles</th>\r\n <th>lock end</th>\r\n </thead>\r\n <tbody>\r\n <tr *ngFor=\"let user of pagination.data\">\r\n <td class=\"command\">\r\n <button\r\n mat-icon-button\r\n type=\"button\"\r\n matTooltip=\"Edit {{ user.userName }}\"\r\n color=\"primary\"\r\n (click)=\"setActiveUser(user)\"\r\n >\r\n <mat-icon>mode_edit</mat-icon>\r\n </button>\r\n </td>\r\n <td class=\"command\">\r\n <button\r\n mat-icon-button\r\n type=\"button\"\r\n matTooltip=\"Delete {{ user.userName }}\"\r\n color=\"warn\"\r\n (click)=\"deleteUser(user)\"\r\n >\r\n <mat-icon>remove_circle</mat-icon>\r\n </button>\r\n </td>\r\n <td>\r\n <img\r\n [src]=\"getGravatarUrl(user.email, 32)\"\r\n [alt]=\"user.userName\"\r\n />\r\n </td>\r\n <td>{{ user.userName }}</td>\r\n <td>{{ user.firstName }}</td>\r\n <td>{{ user.lastName }}</td>\r\n <td>\r\n <a [href]=\"'mailto:' + user.email\">{{ user.email }}</a>\r\n </td>\r\n <td>{{ user.roles?.join(\" \") }}</td>\r\n <td>{{ user.lockoutEnd }}</td>\r\n </tr>\r\n </tbody>\r\n </table>\r\n <!-- pagination -->\r\n <mat-paginator\r\n gdArea=\"pager\"\r\n gdAlignColumns=\"center\"\r\n gdAlignRows=\"start\"\r\n [length]=\"pagination.total\"\r\n [pageSize]=\"pageSize.value\"\r\n [pageSizeOptions]=\"[20, 50, 75, 100]\"\r\n [pageIndex]=\"pagination.currentPage - 1\"\r\n [showFirstLastButtons]=\"true\"\r\n (page)=\"pageChange($event)\"\r\n ></mat-paginator>\r\n </div>\r\n </div>\r\n\r\n <!-- editor -->\r\n <div gdArea=\"editor\" *ngIf=\"active$ | async as active\">\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</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.command{width:24px}table{width:100%;border-collapse:collapse}fieldset{border:1px solid silver;border-radius:8px;padding:16px}\n"] }]
948
- }], ctorParameters: function () { return [{ type: i15.PaginatorPlugin, decorators: [{
949
- type: Inject,
950
- args: [USERS_PAGINATOR]
951
- }] }, { type: UsersQuery }, { type: AuthJwtAccountService }, { type: UsersService }, { type: DialogService }, { type: i2$2.GravatarService }, { type: i1$1.UntypedFormBuilder }]; } });
918
+ args: [{ selector: 'auth-jwt-user-list', template: "<div\n gdAreas=\"filters | progress | list | pager | editor\"\n gdRows=\"80px 64px 2fr 40px 1fr\"\n gdColumns=\"1fr\"\n gdGap=\"8px\"\n>\n <div>\n <!-- filters -->\n <div gdArea=\"filters\">\n <auth-jwt-user-filter></auth-jwt-user-filter>\n </div>\n\n <!-- progress -->\n <div *ngIf=\"loading$ | async\" gdArea=\"progress\">\n <mat-progress-bar mode=\"indeterminate\"></mat-progress-bar>\n </div>\n\n <!-- list -->\n <div gdArea=\"list\" *ngIf=\"pagination$ | async as pagination\">\n <table>\n <thead>\n <td></td>\n <td></td>\n <td></td>\n <th>name</th>\n <th>first</th>\n <th>last</th>\n <th>email</th>\n <th>roles</th>\n <th>lock end</th>\n </thead>\n <tbody>\n <tr *ngFor=\"let user of pagination.data\">\n <td class=\"command\">\n <button\n mat-icon-button\n type=\"button\"\n matTooltip=\"Edit {{ user.userName }}\"\n color=\"primary\"\n (click)=\"setActiveUser(user)\"\n >\n <mat-icon>mode_edit</mat-icon>\n </button>\n </td>\n <td class=\"command\">\n <button\n mat-icon-button\n type=\"button\"\n matTooltip=\"Delete {{ user.userName }}\"\n color=\"warn\"\n (click)=\"deleteUser(user)\"\n >\n <mat-icon>remove_circle</mat-icon>\n </button>\n </td>\n <td>\n <img\n [src]=\"getGravatarUrl(user.email, 32)\"\n [alt]=\"user.userName\"\n />\n </td>\n <td>{{ user.userName }}</td>\n <td>{{ user.firstName }}</td>\n <td>{{ user.lastName }}</td>\n <td>\n <a [href]=\"'mailto:' + user.email\">{{ user.email }}</a>\n </td>\n <td>{{ user.roles.join(\" \") }}</td>\n <td>{{ user.lockoutEnd }}</td>\n </tr>\n </tbody>\n </table>\n <!-- pagination -->\n <mat-paginator\n gdArea=\"pager\"\n gdAlignColumns=\"center\"\n gdAlignRows=\"start\"\n [length]=\"pagination.total\"\n [pageSize]=\"pagination.perPage\"\n [pageSizeOptions]=\"[20, 50, 75, 100]\"\n [pageIndex]=\"pagination.currentPage - 1\"\n [showFirstLastButtons]=\"true\"\n (page)=\"pageChange($event)\"\n ></mat-paginator>\n </div>\n </div>\n\n <!-- editor -->\n <div gdArea=\"editor\" *ngIf=\"active$ | async as active\">\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</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.command{width:24px}table{width:100%;border-collapse:collapse}fieldset{border:1px solid silver;border-radius:8px;padding:16px}\n"] }]
919
+ }], ctorParameters: function () { return [{ type: UserListRepository }, { type: DialogService }, { type: i2$2.GravatarService }]; } });
952
920
 
953
921
  class AuthJwtAdminModule {
954
922
  }
955
- AuthJwtAdminModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.6", ngImport: i0, type: AuthJwtAdminModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
956
- AuthJwtAdminModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "14.2.6", ngImport: i0, type: AuthJwtAdminModule, declarations: [AuthJwtRegistrationComponent,
923
+ AuthJwtAdminModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: AuthJwtAdminModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
924
+ AuthJwtAdminModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.0.0", ngImport: i0, type: AuthJwtAdminModule, declarations: [AuthJwtRegistrationComponent,
957
925
  PasswordStrengthBarComponent,
958
926
  UserFilterComponent,
959
927
  UserListComponent,
@@ -980,7 +948,7 @@ AuthJwtAdminModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", vers
980
948
  PasswordStrengthBarComponent,
981
949
  UserFilterComponent,
982
950
  UserListComponent] });
983
- AuthJwtAdminModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "14.2.6", ngImport: i0, type: AuthJwtAdminModule, imports: [CommonModule,
951
+ AuthJwtAdminModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: AuthJwtAdminModule, imports: [CommonModule,
984
952
  HttpClientModule,
985
953
  FormsModule,
986
954
  RouterModule,
@@ -999,7 +967,7 @@ AuthJwtAdminModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", vers
999
967
  MatTooltipModule,
1000
968
  AuthJwtLoginModule,
1001
969
  NgToolsModule] });
1002
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.6", ngImport: i0, type: AuthJwtAdminModule, decorators: [{
970
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.0", ngImport: i0, type: AuthJwtAdminModule, decorators: [{
1003
971
  type: NgModule,
1004
972
  args: [{
1005
973
  declarations: [