diff --git a/AdminUi/src/AdminUi/ClientApp/src/app/app.module.ts b/AdminUi/src/AdminUi/ClientApp/src/app/app.module.ts index 7b919cad6c..0e149672e9 100644 --- a/AdminUi/src/AdminUi/ClientApp/src/app/app.module.ts +++ b/AdminUi/src/AdminUi/ClientApp/src/app/app.module.ts @@ -54,6 +54,7 @@ import { LoggerWriterService } from "./services/logger-writer-service/logger-wri import { SidebarService } from "./services/sidebar-service/sidebar.service"; import { ApiKeyInterceptor } from "./shared/interceptors/api-key.interceptor"; import { XSRFInterceptor } from "./shared/interceptors/xsrf.interceptor"; +import { IdentitiesOverviewComponent } from "./components/shared/identities-overview/identities-overview.component"; @NgModule({ declarations: [ @@ -72,7 +73,8 @@ import { XSRFInterceptor } from "./shared/interceptors/xsrf.interceptor"; AssignQuotasDialogComponent, ConfirmationDialogComponent, LoginComponent, - ChangeSecretDialogComponent + ChangeSecretDialogComponent, + IdentitiesOverviewComponent ], imports: [ FormsModule, diff --git a/AdminUi/src/AdminUi/ClientApp/src/app/components/client/client-edit/client-edit.component.css b/AdminUi/src/AdminUi/ClientApp/src/app/components/client/client-edit/client-edit.component.css index caaa651c08..cb808b4bdf 100644 --- a/AdminUi/src/AdminUi/ClientApp/src/app/components/client/client-edit/client-edit.component.css +++ b/AdminUi/src/AdminUi/ClientApp/src/app/components/client/client-edit/client-edit.component.css @@ -79,6 +79,33 @@ color: #fff; } +.client-accordion { + margin: 0px 0px 0px 5px !important; +} + +.details-expansion-panel-header { + background: #17428d !important; +} + +.details-expansion-panel-header:hover { + background: #11337a !important; +} +.details-expansion-panel-header.mat-expansion-panel-header.mat-expanded { + border-radius: 4px 4px 0px 0px; +} + +.details-panel-header-title { + color: white !important; +} + +.details-panel-header-desc { + color: rgba(255, 255, 255, 0.54) !important; +} + +:host ::ng-deep .details-expansion-panel-header > .mat-expansion-indicator::after { + color: white !important; +} + .auto-height { height: auto; } diff --git a/AdminUi/src/AdminUi/ClientApp/src/app/components/client/client-edit/client-edit.component.html b/AdminUi/src/AdminUi/ClientApp/src/app/components/client/client-edit/client-edit.component.html index 55a1276361..8859a8d477 100644 --- a/AdminUi/src/AdminUi/ClientApp/src/app/components/client/client-edit/client-edit.component.html +++ b/AdminUi/src/AdminUi/ClientApp/src/app/components/client/client-edit/client-edit.component.html @@ -78,6 +78,19 @@

{{ editMode ? headerEdit : headerCreate }}

+ + + + + {{ headerIdentities }} + + + {{ headerIdentitiesDescription }} + + + + +
diff --git a/AdminUi/src/AdminUi/ClientApp/src/app/components/client/client-edit/client-edit.component.ts b/AdminUi/src/AdminUi/ClientApp/src/app/components/client/client-edit/client-edit.component.ts index b623d9f490..4a20bf01f2 100644 --- a/AdminUi/src/AdminUi/ClientApp/src/app/components/client/client-edit/client-edit.component.ts +++ b/AdminUi/src/AdminUi/ClientApp/src/app/components/client/client-edit/client-edit.component.ts @@ -14,8 +14,13 @@ import { PagedHttpResponseEnvelope } from "src/app/utils/paged-http-response-env export class ClientEditComponent { public headerEdit: string; public headerCreate: string; + public headerDescriptionEdit: string; public headerDescriptionCreate: string; + + public headerIdentities: string; + public headerIdentitiesDescription: string; + public showPassword: boolean; public clientId?: string; public editMode: boolean; @@ -35,6 +40,8 @@ export class ClientEditComponent { this.headerEdit = "Edit Client"; this.headerDescriptionCreate = "Please fill the form below to create your Client"; this.headerDescriptionEdit = "Perform your desired changes and save to edit your Client"; + this.headerIdentities = "Identities"; + this.headerIdentitiesDescription = "View and manage Identities created by this Client."; this.editMode = false; this.loading = true; this.disabled = false; diff --git a/AdminUi/src/AdminUi/ClientApp/src/app/components/quotas/identity/identity-list/identity-list.component.css b/AdminUi/src/AdminUi/ClientApp/src/app/components/quotas/identity/identity-list/identity-list.component.css index f54fd74d0b..f672c80a72 100644 --- a/AdminUi/src/AdminUi/ClientApp/src/app/components/quotas/identity/identity-list/identity-list.component.css +++ b/AdminUi/src/AdminUi/ClientApp/src/app/components/quotas/identity/identity-list/identity-list.component.css @@ -1,72 +1,8 @@ -.mat-column-address, -.mat-column-address-filter, -.mat-column-clientId, -.mat-column-publicKey { - width: 25%; - word-break: break-all; -} - -.mat-column-createdAt, -.mat-column-created-at-filter, -.mat-column-last-login-filter, -.mat-column-lastLoginAt { - max-width: 330px; -} - -.mat-column-tier-filter, -.mat-column-tierName, -.mat-column-createdWithClient, -.mat-column-client-filter { - max-width: 230px; -} - -.mat-column-number-of-devices-filter, -.mat-column-numberOfDevices, -.mat-column-identity-version-filter, -.mat-column-identityVersion, -.mat-column-datawallet-version-filter, -.mat-column-datawalletVersion { - max-width: 200px; -} - .disabled-container { pointer-events: none; opacity: 0.4; } -.inline-action-buttons ::ng-deep .mat-mdc-form-field-icon-suffix { - display: inherit; -} - -.mat-column-address { - width: 26.5em; -} - -.tier-navigation { - text-decoration: underline; - color: blue; -} - -.loading { - display: flex; - justify-content: center; - align-items: center; - z-index: 999; - position: absolute; - margin-top: -60px; - width: 100%; - height: 100%; -} - -.no-data { - padding: 25px; -} - -.mat-mdc-row:hover { - background-color: #ddd !important; - cursor: pointer; -} - .card-header { border-radius: 3px; padding: 15px; @@ -76,11 +12,6 @@ margin: 15px 15px -50px 15px; } -.filter-cell { - display: flex; - justify-content: space-between; -} - .header-description { color: #fff; } @@ -88,50 +19,3 @@ .header-title { color: #fff; } - -.action-buttons { - margin-top: 50px; - display: flex; - justify-content: flex-end; -} - -@media screen and (max-width: 960px) { - .card-header { - margin: 15px 15px -20px 15px; - } - - .mat-mdc-table .mat-mdc-header-row { - display: none; - } - - .mat-mdc-table .mat-mdc-row { - display: flex; - flex-wrap: wrap; - height: auto; - border-bottom: 1px solid #ddd; - } - - .mat-mdc-table .mat-mdc-cell { - width: 100%; - border-bottom: 0px solid #ddd; - font-size: 1em; - min-height: 30px; - margin-bottom: 4%; - word-break: break-all; - white-space: pre-wrap; - } - - .mat-mdc-table .mat-mdc-cell:before { - content: attr(data-label) ":"; - float: left; - font-weight: 500; - } - - .mat-mdc-table .mat-mdc-cell:first-child { - margin-top: 25px; - } - - .mat-mdc-table .mat-mdc-row:last-child { - border-bottom: 0px; - } -} diff --git a/AdminUi/src/AdminUi/ClientApp/src/app/components/quotas/identity/identity-list/identity-list.component.html b/AdminUi/src/AdminUi/ClientApp/src/app/components/quotas/identity/identity-list/identity-list.component.html index 01715ca0bf..624f634cc2 100644 --- a/AdminUi/src/AdminUi/ClientApp/src/app/components/quotas/identity/identity-list/identity-list.component.html +++ b/AdminUi/src/AdminUi/ClientApp/src/app/components/quotas/identity/identity-list/identity-list.component.html @@ -2,186 +2,8 @@

{{ header }}

{{ headerDescription }}

- + -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Address - {{ identity.address }} - Tier{{ identity.tier.name }}Created with Client - {{ identity.createdWithClient }} - Number of Devices - {{ identity.numberOfDevices }} - Created At - {{ identity.createdAt | date }} - Last Login At - {{ identity.lastLoginAt | date }} - Datawallet Version - {{ identity.datawalletVersion }} - Identity Version - {{ identity.identityVersion }} - - - - - - - - Tiers - - {{ tier.name }} - - - - - Clients - - {{ client.displayName }} - - - -
- - - {{ operator }} - - - - - -
-
-
- - - {{ operator }} - - - - Choose a date - - - - - -
-
-
- - - {{ operator }} - - - - Choose a date - - - - - -
-
-
- - - {{ operator }} - - - - - -
-
-
- - - {{ operator }} - - - - - -
-
No identities found.
- - +
diff --git a/AdminUi/src/AdminUi/ClientApp/src/app/components/quotas/identity/identity-list/identity-list.component.ts b/AdminUi/src/AdminUi/ClientApp/src/app/components/quotas/identity/identity-list/identity-list.component.ts index 7f942f241a..596782b58d 100644 --- a/AdminUi/src/AdminUi/ClientApp/src/app/components/quotas/identity/identity-list/identity-list.component.ts +++ b/AdminUi/src/AdminUi/ClientApp/src/app/components/quotas/identity/identity-list/identity-list.component.ts @@ -1,14 +1,4 @@ -import { Component, ElementRef, ViewChild } from "@angular/core"; -import { MatPaginator, PageEvent } from "@angular/material/paginator"; -import { MatSnackBar } from "@angular/material/snack-bar"; -import { Router } from "@angular/router"; -import { NGXLogger } from "ngx-logger"; -import { debounceTime, distinctUntilChanged, filter, fromEvent, tap } from "rxjs"; -import { ClientOverview, ClientService } from "src/app/services/client-service/client-service"; -import { IdentityOverview, IdentityOverviewFilter, IdentityService } from "src/app/services/identity-service/identity.service"; -import { TierOverview, TierService } from "src/app/services/tier-service/tier.service"; -import { ODataResponse } from "src/app/utils/odata-response"; -import { PagedHttpResponseEnvelope } from "src/app/utils/paged-http-response-envelope"; +import { Component } from "@angular/core"; @Component({ selector: "app-identity-list", @@ -16,222 +6,15 @@ import { PagedHttpResponseEnvelope } from "src/app/utils/paged-http-response-env styleUrls: ["./identity-list.component.css"] }) export class IdentityListComponent { - @ViewChild(MatPaginator) public paginator!: MatPaginator; - @ViewChild("addressFilter", { static: false }) public set addressFilter(input: ElementRef | undefined) { - this.debounceFilter(input, "address"); - } - @ViewChild("numberOfDevicesFilter", { static: false }) public set numberOfDevicesFilter(input: ElementRef | undefined) { - this.debounceFilter(input, "numberOfDevices"); - } - @ViewChild("datawalletVersionFilter", { static: false }) public set datawalletVersionFilter(input: ElementRef | undefined) { - this.debounceFilter(input, "datawalletVersion"); - } - @ViewChild("identityVersionFilter", { static: false }) public set identityVersionFilter(input: ElementRef | undefined) { - this.debounceFilter(input, "identityVersion"); - } - public header: string; public headerDescription: string; - public identities: IdentityOverview[]; - - public totalRecords: number; - public pageSize: number; - public pageIndex: number; - - public loading = false; - - public displayedColumns: string[] = ["address", "tierName", "createdWithClient", "numberOfDevices", "createdAt", "lastLoginAt", "datawalletVersion", "identityVersion"]; - public displayedColumnFilters: string[] = [ - "address-filter", - "tier-filter", - "client-filter", - "number-of-devices-filter", - "created-at-filter", - "last-login-filter", - "datawallet-version-filter", - "identity-version-filter" - ]; - public operators: string[] = ["=", "<", ">", ">=", "<="]; + public loading: boolean; - public filter: IdentityOverviewFilter; - public tiers: TierOverview[]; - public clients: ClientOverview[]; - - public constructor( - private readonly router: Router, - private readonly snackBar: MatSnackBar, - private readonly identityService: IdentityService, - private readonly tierService: TierService, - private readonly clientService: ClientService, - private readonly logger: NGXLogger - ) { + public constructor() { this.header = "Identities"; this.headerDescription = "A list of existing Identities"; - this.identities = []; - - this.totalRecords = 0; - this.pageSize = 10; - this.pageIndex = 0; - - this.filter = { createdAt: { operator: "=" }, numberOfDevices: { operator: "=" }, identityVersion: { operator: "=" }, lastLoginAt: { operator: "=" }, datawalletVersion: { operator: "=" } }; - this.tiers = []; - this.clients = []; - - this.loading = true; - } - - public ngOnInit(): void { - this.getIdentities(); - this.getTiers(); - this.getClients(); - } - - private debounceFilter(filterElement: ElementRef | undefined, filterName: string): void { - if (filterElement !== undefined) { - fromEvent(filterElement.nativeElement, "keyup") - .pipe( - filter(Boolean), - debounceTime(750), - distinctUntilChanged(), - tap((_) => { - this.onFilterChange(filterName); - }) - ) - .subscribe(); - } - } - - private getTiers(): void { - this.tierService.getTiers().subscribe({ - next: (data: PagedHttpResponseEnvelope) => { - this.tiers = data.result; - }, - complete: () => (this.loading = false), - error: (err: any) => { - this.loading = false; - const errorMessage = err.error?.error?.message ?? err.message; - this.snackBar.open(errorMessage, "Dismiss", { - verticalPosition: "top", - horizontalPosition: "center" - }); - } - }); - } - - private getClients(): void { - this.clientService.getClients().subscribe({ - next: (data: PagedHttpResponseEnvelope) => { - this.clients = data.result; - }, - complete: () => (this.loading = false), - error: (err: any) => { - this.loading = false; - const errorMessage = err.error?.error?.message ?? err.message; - this.snackBar.open(errorMessage, "Dismiss", { - verticalPosition: "top", - horizontalPosition: "center" - }); - } - }); - } - - private getIdentities(): void { - this.loading = true; - this.identityService.getIdentities(this.filter, this.pageIndex, this.pageSize).subscribe({ - next: (data: ODataResponse) => { - this.identities = data.value; - this.totalRecords = data.value.length; - }, - complete: () => (this.loading = false), - error: (err: any) => { - this.loading = false; - const errorMessage = err.error?.error?.message ?? err.message; - this.snackBar.open(errorMessage, "Dismiss", { - verticalPosition: "top", - horizontalPosition: "center" - }); - } - }); - } - - public pageChangeEvent(event: PageEvent): void { - this.pageIndex = event.pageIndex; - this.pageSize = event.pageSize; - this.getIdentities(); - } - - public async editIdentity(identityAddress: string): Promise { - await this.router.navigate([`/identities/${identityAddress}`]); - } - - public async goToTier(tierId: string): Promise { - await this.router.navigate([`/tiers/${tierId}`]); - } - - public onFilterChange(filter: string): void { - switch (filter) { - case "address": - if (this.filter.address!.length > 2) this.getIdentities(); - break; - case "tiers": - case "clients": - this.getIdentities(); - break; - case "numberOfDevices": - if ( - (this.filter.numberOfDevices.operator !== undefined && this.filter.numberOfDevices.value !== undefined) || - (this.filter.numberOfDevices.operator !== undefined && this.filter.numberOfDevices.value === undefined) - ) { - this.getIdentities(); - } - break; - case "createdAt": - if (this.filter.createdAt.operator !== undefined && this.filter.createdAt.operator !== "" && this.filter.createdAt.value !== undefined) this.getIdentities(); - break; - case "lastLoginAt": - if (this.filter.lastLoginAt.operator !== undefined && this.filter.lastLoginAt.operator !== "" && this.filter.lastLoginAt.value !== undefined) this.getIdentities(); - break; - case "datawalletVersion": - if ( - (this.filter.datawalletVersion.operator !== undefined && this.filter.datawalletVersion.value !== undefined) || - (this.filter.datawalletVersion.operator !== undefined && this.filter.datawalletVersion.value === undefined) - ) { - this.getIdentities(); - } - break; - case "identityVersion": - if ( - (this.filter.identityVersion.operator !== undefined && this.filter.identityVersion.value !== undefined) || - (this.filter.identityVersion.operator !== undefined && this.filter.identityVersion.value === undefined) - ) { - this.getIdentities(); - } - break; - default: - this.logger.error(`OnFilterChange: Invalid filter name: ${filter}`); - break; - } - } - - public clearFilter(filter: string): void { - switch (filter) { - case "address": - this.filter.address = ""; - this.getIdentities(); - break; - case "createdAt": - this.filter.createdAt.value = undefined; - if (this.filter.createdAt.operator !== undefined) this.getIdentities(); - break; - case "lastLoginAt": - this.filter.lastLoginAt.value = undefined; - if (this.filter.lastLoginAt.operator !== undefined) this.getIdentities(); - break; - default: - this.logger.error(`ClearFilter: Invalid filter name: ${filter}`); - break; - } + this.loading = false; } } diff --git a/AdminUi/src/AdminUi/ClientApp/src/app/components/shared/identities-overview/identities-overview.component.css b/AdminUi/src/AdminUi/ClientApp/src/app/components/shared/identities-overview/identities-overview.component.css new file mode 100644 index 0000000000..9461619e64 --- /dev/null +++ b/AdminUi/src/AdminUi/ClientApp/src/app/components/shared/identities-overview/identities-overview.component.css @@ -0,0 +1,144 @@ +.mat-column-address, +.mat-column-address-filter, +.mat-column-clientId, +.mat-column-publicKey { + width: 25%; + word-break: break-all; +} + +.mat-column-createdAt, +.mat-column-created-at-filter, +.mat-column-last-login-filter, +.mat-column-lastLoginAt { + max-width: 330px; +} + +.mat-column-tier-filter, +.mat-column-tierName, +.mat-column-createdWithClient, +.mat-column-client-filter { + max-width: 230px; +} + +.mat-column-number-of-devices-filter, +.mat-column-numberOfDevices, +.mat-column-identity-version-filter, +.mat-column-identityVersion, +.mat-column-datawallet-version-filter, +.mat-column-datawalletVersion { + max-width: 200px; +} + +.disabled-container { + pointer-events: none; + opacity: 0.4; +} + +.inline-action-buttons ::ng-deep .mat-mdc-form-field-icon-suffix { + display: inherit; +} + +.mat-column-address { + width: 26.5em; +} + +.tier-navigation { + text-decoration: underline; + color: blue; +} + +.loading { + display: flex; + justify-content: center; + align-items: center; + z-index: 999; + position: absolute; + margin-top: -60px; + width: 100%; + height: 100%; +} + +.no-data { + padding: 25px; +} + +.mat-mdc-row:hover { + background-color: #ddd !important; + cursor: pointer; +} + +.card-header { + border-radius: 3px; + padding: 15px; + background-color: #17428d; + position: relative; + z-index: 1; + margin: 15px 15px -50px 15px; +} + +.filter-cell { + display: flex; + justify-content: space-between; +} + +.header-description { + color: #fff; +} + +.header-title { + color: #fff; +} + +.action-buttons { + margin-top: 50px; + display: flex; + justify-content: flex-end; +} + +@media screen and (max-width: 960px) { + .card-header { + margin: 15px 15px -20px 15px; + } + + .mat-mdc-table .mat-mdc-header-row { + display: none; + } + + .mat-mdc-table .mat-mdc-row { + display: flex; + flex-wrap: wrap; + height: auto; + border-bottom: 1px solid #ddd; + } + + .mat-mdc-table .mat-mdc-cell { + width: 100%; + border-bottom: 0px solid #ddd; + font-size: 1em; + min-height: 30px; + margin-bottom: 4%; + word-break: break-all; + white-space: pre-wrap; + } + + .mat-mdc-table .mat-mdc-cell:before { + content: attr(data-label) ":"; + float: left; + font-weight: 500; + } + + .mat-mdc-table .mat-mdc-cell:first-child { + margin-top: 25px; + } + + .mat-mdc-table .mat-mdc-row:last-child { + border-bottom: 0px; + } +} + +@media screen and (min-width: 960px) { + .mat-mdc-header-cell, + .mat-mdc-cell { + text-align: center; + } +} diff --git a/AdminUi/src/AdminUi/ClientApp/src/app/components/shared/identities-overview/identities-overview.component.html b/AdminUi/src/AdminUi/ClientApp/src/app/components/shared/identities-overview/identities-overview.component.html new file mode 100644 index 0000000000..3819c49c2a --- /dev/null +++ b/AdminUi/src/AdminUi/ClientApp/src/app/components/shared/identities-overview/identities-overview.component.html @@ -0,0 +1,181 @@ +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Address + {{ identity.address }} + Tier{{ identity.tier.name }}Created with Client + {{ identity.createdWithClient }} + Number of Devices + {{ identity.numberOfDevices }} + Created At + {{ identity.createdAt | date }} + Last Login At + {{ identity.lastLoginAt | date }} + Datawallet Version + {{ identity.datawalletVersion }} + Identity Version + {{ identity.identityVersion }} + + + + + + + + Tiers + + {{ tier.name }} + + + + + Clients + + {{ client.displayName }} + + + +
+ + + {{ operator }} + + + + + +
+
+
+ + + {{ operator }} + + + + Choose a date + + + + + +
+
+
+ + + {{ operator }} + + + + Choose a date + + + + + +
+
+
+ + + {{ operator }} + + + + + +
+
+
+ + + {{ operator }} + + + + + +
+
No identities found.
+ + +
diff --git a/AdminUi/src/AdminUi/ClientApp/src/app/components/shared/identities-overview/identities-overview.component.spec.ts b/AdminUi/src/AdminUi/ClientApp/src/app/components/shared/identities-overview/identities-overview.component.spec.ts new file mode 100644 index 0000000000..987bf345e8 --- /dev/null +++ b/AdminUi/src/AdminUi/ClientApp/src/app/components/shared/identities-overview/identities-overview.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from "@angular/core/testing"; + +import { IdentitiesOverviewComponent } from "./identities-overview.component"; + +describe("IdentitiesOverviewComponent", function () { + let component: IdentitiesOverviewComponent; + let fixture: ComponentFixture; + + beforeEach(function () { + TestBed.configureTestingModule({ + declarations: [IdentitiesOverviewComponent] + }); + fixture = TestBed.createComponent(IdentitiesOverviewComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it("should create", async function () { + await expect(component).toBeTruthy(); + }); +}); diff --git a/AdminUi/src/AdminUi/ClientApp/src/app/components/shared/identities-overview/identities-overview.component.ts b/AdminUi/src/AdminUi/ClientApp/src/app/components/shared/identities-overview/identities-overview.component.ts new file mode 100644 index 0000000000..72d1e742a8 --- /dev/null +++ b/AdminUi/src/AdminUi/ClientApp/src/app/components/shared/identities-overview/identities-overview.component.ts @@ -0,0 +1,244 @@ +import { Component, ElementRef, Input, ViewChild } from "@angular/core"; +import { MatPaginator, PageEvent } from "@angular/material/paginator"; +import { MatSnackBar } from "@angular/material/snack-bar"; +import { Router } from "@angular/router"; +import { NGXLogger } from "ngx-logger"; +import { fromEvent, filter, debounceTime, distinctUntilChanged, tap } from "rxjs"; +import { ClientOverview, ClientService } from "src/app/services/client-service/client-service"; +import { IdentityOverview, IdentityOverviewFilter, IdentityService } from "src/app/services/identity-service/identity.service"; +import { TierOverview, TierService } from "src/app/services/tier-service/tier.service"; +import { ODataResponse } from "src/app/utils/odata-response"; +import { PagedHttpResponseEnvelope } from "src/app/utils/paged-http-response-envelope"; + +@Component({ + selector: "app-identities-overview", + templateUrl: "./identities-overview.component.html", + styleUrls: ["./identities-overview.component.css"] +}) +export class IdentitiesOverviewComponent { + @Input() public clientId?: string; + + @ViewChild(MatPaginator) public paginator!: MatPaginator; + @ViewChild("addressFilter", { static: false }) public set addressFilter(input: ElementRef | undefined) { + this.debounceFilter(input, "address"); + } + @ViewChild("numberOfDevicesFilter", { static: false }) public set numberOfDevicesFilter(input: ElementRef | undefined) { + this.debounceFilter(input, "numberOfDevices"); + } + @ViewChild("datawalletVersionFilter", { static: false }) public set datawalletVersionFilter(input: ElementRef | undefined) { + this.debounceFilter(input, "datawalletVersion"); + } + @ViewChild("identityVersionFilter", { static: false }) public set identityVersionFilter(input: ElementRef | undefined) { + this.debounceFilter(input, "identityVersion"); + } + + public identities: IdentityOverview[]; + + public totalRecords: number; + public pageSize: number; + public pageIndex: number; + + public loading = false; + + public displayedColumns: string[] = ["address", "tierName", "createdWithClient", "numberOfDevices", "createdAt", "lastLoginAt", "datawalletVersion", "identityVersion"]; + public displayedColumnFilters: string[] = [ + "address-filter", + "tier-filter", + "client-filter", + "number-of-devices-filter", + "created-at-filter", + "last-login-filter", + "datawallet-version-filter", + "identity-version-filter" + ]; + public operators: string[] = ["=", "<", ">", ">=", "<="]; + + public filter: IdentityOverviewFilter; + public tiers: TierOverview[]; + public clients: ClientOverview[]; + + public constructor( + private readonly router: Router, + private readonly snackBar: MatSnackBar, + private readonly identityService: IdentityService, + private readonly tierService: TierService, + private readonly clientService: ClientService, + private readonly logger: NGXLogger + ) { + this.identities = []; + + this.totalRecords = 0; + this.pageSize = 10; + this.pageIndex = 0; + + this.filter = { createdAt: { operator: "=" }, numberOfDevices: { operator: "=" }, identityVersion: { operator: "=" }, lastLoginAt: { operator: "=" }, datawalletVersion: { operator: "=" } }; + this.tiers = []; + this.clients = []; + + this.loading = true; + } + + public ngOnInit(): void { + if (this.clientId) { + this.setClientFilter(); + } else { + this.getClients(); + } + + this.getIdentities(); + this.getTiers(); + } + + private debounceFilter(filterElement: ElementRef | undefined, filterName: string): void { + if (filterElement !== undefined) { + fromEvent(filterElement.nativeElement, "keyup") + .pipe( + filter(Boolean), + debounceTime(750), + distinctUntilChanged(), + tap((_) => { + this.onFilterChange(filterName); + }) + ) + .subscribe(); + } + } + + private getTiers(): void { + this.tierService.getTiers().subscribe({ + next: (data: PagedHttpResponseEnvelope) => { + this.tiers = data.result; + }, + complete: () => (this.loading = false), + error: (err: any) => { + this.loading = false; + const errorMessage = err.error?.error?.message ?? err.message; + this.snackBar.open(errorMessage, "Dismiss", { + verticalPosition: "top", + horizontalPosition: "center" + }); + } + }); + } + + private setClientFilter(): void { + this.filter.clients = [this.clientId!]; + this.displayedColumns = this.displayedColumns.filter((dc) => dc !== "createdWithClient"); + this.displayedColumnFilters = this.displayedColumnFilters.filter((dcf) => dcf !== "client-filter"); + } + + private getClients(): void { + this.clientService.getClients().subscribe({ + next: (data: PagedHttpResponseEnvelope) => { + this.clients = data.result; + }, + complete: () => (this.loading = false), + error: (err: any) => { + this.loading = false; + const errorMessage = err.error?.error?.message ?? err.message; + this.snackBar.open(errorMessage, "Dismiss", { + verticalPosition: "top", + horizontalPosition: "center" + }); + } + }); + } + + private getIdentities(): void { + this.loading = true; + this.identityService.getIdentities(this.filter, this.pageIndex, this.pageSize).subscribe({ + next: (data: ODataResponse) => { + this.identities = data.value; + this.totalRecords = data.value.length; + }, + complete: () => (this.loading = false), + error: (err: any) => { + this.loading = false; + const errorMessage = err.error?.error?.message ?? err.message; + this.snackBar.open(errorMessage, "Dismiss", { + verticalPosition: "top", + horizontalPosition: "center" + }); + } + }); + } + + public pageChangeEvent(event: PageEvent): void { + this.pageIndex = event.pageIndex; + this.pageSize = event.pageSize; + this.getIdentities(); + } + + public async editIdentity(identityAddress: string): Promise { + await this.router.navigate([`/identities/${identityAddress}`]); + } + + public async goToTier(tierId: string): Promise { + await this.router.navigate([`/tiers/${tierId}`]); + } + + public onFilterChange(filter: string): void { + switch (filter) { + case "address": + if (this.filter.address!.length > 2) this.getIdentities(); + break; + case "tiers": + case "clients": + this.getIdentities(); + break; + case "numberOfDevices": + if ( + (this.filter.numberOfDevices.operator !== undefined && this.filter.numberOfDevices.value !== undefined) || + (this.filter.numberOfDevices.operator !== undefined && this.filter.numberOfDevices.value === undefined) + ) { + this.getIdentities(); + } + break; + case "createdAt": + if (this.filter.createdAt.operator !== undefined && this.filter.createdAt.operator !== "" && this.filter.createdAt.value !== undefined) this.getIdentities(); + break; + case "lastLoginAt": + if (this.filter.lastLoginAt.operator !== undefined && this.filter.lastLoginAt.operator !== "" && this.filter.lastLoginAt.value !== undefined) this.getIdentities(); + break; + case "datawalletVersion": + if ( + (this.filter.datawalletVersion.operator !== undefined && this.filter.datawalletVersion.value !== undefined) || + (this.filter.datawalletVersion.operator !== undefined && this.filter.datawalletVersion.value === undefined) + ) { + this.getIdentities(); + } + break; + case "identityVersion": + if ( + (this.filter.identityVersion.operator !== undefined && this.filter.identityVersion.value !== undefined) || + (this.filter.identityVersion.operator !== undefined && this.filter.identityVersion.value === undefined) + ) { + this.getIdentities(); + } + break; + default: + this.logger.error(`OnFilterChange: Invalid filter name: ${filter}`); + break; + } + } + + public clearFilter(filter: string): void { + switch (filter) { + case "address": + this.filter.address = ""; + this.getIdentities(); + break; + case "createdAt": + this.filter.createdAt.value = undefined; + if (this.filter.createdAt.operator !== undefined) this.getIdentities(); + break; + case "lastLoginAt": + this.filter.lastLoginAt.value = undefined; + if (this.filter.lastLoginAt.operator !== undefined) this.getIdentities(); + break; + default: + this.logger.error(`ClearFilter: Invalid filter name: ${filter}`); + break; + } + } +}