feat(console): project view model, remove orgid from routes (#263)

* proto gen

* fix: remove type from project lists (#256)

* fix: remove type from project lists

* Update user-detail.component.ts

* fix: remove add project

Co-authored-by: Livio Amstutz <[email protected]>

* fix project view model

* regen mgmt proto

* rm orgid from route, switch to project view

* chore(deps-dev): bump @types/jasmine from 3.5.10 to 3.5.11 in /console (#252)

Bumps [@types/jasmine](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/jasmine) from 3.5.10 to 3.5.11.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/jasmine)

Signed-off-by: dependabot[bot] <[email protected]>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps-dev): bump @angular-devkit/build-angular in /console (#251)

Bumps [@angular-devkit/build-angular](https://github.com/angular/angular-cli) from 0.901.7 to 0.901.9.
- [Release notes](https://github.com/angular/angular-cli/releases)
- [Commits](https://github.com/angular/angular-cli/commits)

Signed-off-by: dependabot[bot] <[email protected]>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): bump moment from 2.26.0 to 2.27.0 in /console (#250)

Bumps [moment](https://github.com/moment/moment) from 2.26.0 to 2.27.0.
- [Release notes](https://github.com/moment/moment/releases)
- [Changelog](https://github.com/moment/moment/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/moment/moment/compare/2.26.0...2.27.0)

Signed-off-by: dependabot[bot] <[email protected]>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps-dev): bump karma from 5.0.9 to 5.1.0 in /console (#218)

Bumps [karma](https://github.com/karma-runner/karma) from 5.0.9 to 5.1.0.
- [Release notes](https://github.com/karma-runner/karma/releases)
- [Changelog](https://github.com/karma-runner/karma/blob/master/CHANGELOG.md)
- [Commits](https://github.com/karma-runner/karma/compare/v5.0.9...v5.1.0)

Signed-off-by: dependabot[bot] <[email protected]>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): bump ngx-moment from 3.5.0 to 4.0.1 in /console (#219)

Bumps [ngx-moment](https://github.com/urish/ngx-moment) from 3.5.0 to 4.0.1.
- [Release notes](https://github.com/urish/ngx-moment/releases)
- [Changelog](https://github.com/urish/ngx-moment/blob/master/CHANGELOG.md)
- [Commits](https://github.com/urish/ngx-moment/compare/3.5.0...4.0.1)

Signed-off-by: dependabot[bot] <[email protected]>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Max Peintner <[email protected]>

* chore(deps-dev): bump @angular/language-service in /console (#217)

Bumps [@angular/language-service](https://github.com/angular/angular/tree/HEAD/packages/language-service) from 9.1.10 to 9.1.11.
- [Release notes](https://github.com/angular/angular/releases)
- [Changelog](https://github.com/angular/angular/blob/master/CHANGELOG.md)
- [Commits](https://github.com/angular/angular/commits/9.1.11/packages/language-service)

Signed-off-by: dependabot[bot] <[email protected]>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Max Peintner <[email protected]>

* chore(deps-dev): bump @angular/cli from 9.1.7 to 9.1.9 in /console (#249)

Bumps [@angular/cli](https://github.com/angular/angular-cli) from 9.1.7 to 9.1.9.
- [Release notes](https://github.com/angular/angular-cli/releases)
- [Commits](https://github.com/angular/angular-cli/compare/v9.1.7...v9.1.9)

Signed-off-by: dependabot[bot] <[email protected]>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Max Peintner <[email protected]>

* set partial user profile

* fix org routing

* auth user loginnames, i18n

* fix clipboard, secret regeneration

* project role required field

* show change editor

* show granted project grid, remove add button

* hide meta overflow

Co-authored-by: Fabi <[email protected]>
Co-authored-by: Livio Amstutz <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This commit is contained in:
Max Peintner
2020-06-23 18:56:29 +02:00
committed by GitHub
co-authored by dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Fabi Livio Amstutz dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
parent 3cd3a238c2
commit e54778828e
59 changed files with 4637 additions and 3316 deletions
@@ -6,8 +6,7 @@
<h1>{{ 'APP.PAGES.TITLE' | translate }} {{app?.name}}</h1>
<p class="desc">{{ 'APP.PAGES.DESCRIPTION' | translate }}</p>
<p *ngIf="isZitadel" class="zitadel-warning">This belongs to Zitadel project. If you change something, Zitadel
may not behave as intended!</p>
<p *ngIf="isZitadel" class="zitadel-warning">{{'PROJECT.PAGES.ZITADELPROJECT' | translate}}</p>
</div>
<span *ngIf="errorMessage" class="err-container">{{errorMessage}}</span>
@@ -235,7 +235,8 @@ export class AppDetailComponent implements OnInit, OnDestroy {
}
public regenerateOIDCClientSecret(): void {
this.projectService.RegenerateOIDCClientSecret(this.app.id).then((data: OIDCConfig) => {
console.log(this.app.id, this.projectId);
this.projectService.RegenerateOIDCClientSecret(this.app.id, this.projectId).then((data: OIDCConfig) => {
console.log(data.toObject());
this.toast.showInfo('OIDC Secret Regenerated');
this.dialog.open(AppSecretDialogComponent, {
@@ -6,10 +6,10 @@
<p *ngIf="data.clientId">ClientId: {{data.clientId}}</p>
<div *ngIf="data.clientSecret" class="flex">
<button matTooltip="copy to clipboard" (click)="copytoclipboard(data.clientSecret)" mat-icon-button>
<mat-icon *ngIf="!copied" svgIcon="mdi_content_copy"></mat-icon>
<mat-icon *ngIf="copied">check</mat-icon>
<button color="primary" [disabled]="copied == data.clientSecret" matTooltip="copy to clipboard"
(click)="copytoclipboard(data.clientSecret)" mat-icon-button>
<i *ngIf="copied != data.clientSecret" class="las la-clipboard"></i>
<i *ngIf="copied == data.clientSecret" class="las la-clipboard-check"></i>
</button>
<span class="secret">{{data.clientSecret}}</span>
</div>
@@ -7,7 +7,7 @@ import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
styleUrls: ['./app-secret-dialog.component.scss'],
})
export class AppSecretDialogComponent {
public copied: boolean = false;
public copied: string = '';
constructor(public dialogRef: MatDialogRef<AppSecretDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: any) { }
@@ -27,9 +27,9 @@ export class AppSecretDialogComponent {
selBox.select();
document.execCommand('copy');
document.body.removeChild(selBox);
this.copied = true;
this.copied = value;
setTimeout(() => {
this.copied = false;
this.copied = '';
}, 3000);
}
}
@@ -87,7 +87,7 @@ export class OrgContributorsComponent implements OnInit {
public showDetail(): void {
if (this.org?.state === OrgState.ORGSTATE_ACTIVE) {
this.router.navigate(['orgs', this.org.id, 'members']);
this.router.navigate(['org/members']);
}
}
}
@@ -46,6 +46,13 @@
<form [formGroup]="userForm" (ngSubmit)="finish()" class="form">
<div class="content">
<p class="section">{{ 'USER.CREATE.NAMEANDEMAILSECTION' | translate }}</p>
<mat-form-field class="formfield">
<mat-label>{{ 'USER.PROFILE.USERNAME' | translate }}</mat-label>
<input matInput formControlName="userName" required />
<mat-error *ngIf="userName?.invalid && userName?.errors?.required">
{{ 'USER.VALIDATION.REQUIRED' | translate }}
</mat-error>
</mat-form-field>
<mat-form-field class="formfield">
<mat-label>{{ 'USER.PROFILE.EMAIL' | translate }}</mat-label>
<input matInput formControlName="email" required />
@@ -143,4 +150,4 @@
{{'CONTINUE' | translate}}
</button>
</ng-container>
</div>
</div>
@@ -78,11 +78,11 @@ export class OrgCreateComponent {
validators.push(Validators.pattern(/[0-9]/g));
}
if (this.policy.hasSymbol) {
// All characters that are not a digit or an English letter \W or a whitespace \S
validators.push(Validators.pattern(/[\W\S]/));
validators.push(Validators.pattern(/[^a-z0-9]/gi));
}
this.userForm = this.fb.group({
userName: ['', [Validators.required]],
firstName: ['', [Validators.required]],
lastName: ['', [Validators.required]],
email: ['', [Validators.required]],
@@ -96,6 +96,7 @@ export class OrgCreateComponent {
console.log('no password complexity policy defined!');
console.error(error);
this.userForm = this.fb.group({
userName: ['', [Validators.required]],
firstName: ['', [Validators.required]],
lastName: ['', [Validators.required]],
email: ['', [Validators.required]],
@@ -117,6 +118,7 @@ export class OrgCreateComponent {
createOrgRequest.setDomain(this.domain?.value);
const registerUserRequest: CreateUserRequest = new CreateUserRequest();
registerUserRequest.setUserName(this.userName?.value);
registerUserRequest.setEmail(this.email?.value);
registerUserRequest.setFirstName(this.firstName?.value);
registerUserRequest.setLastName(this.lastName?.value);
@@ -151,6 +153,9 @@ export class OrgCreateComponent {
return this.orgForm.get('domain');
}
public get userName(): AbstractControl | null {
return this.userForm.get('userName');
}
public get firstName(): AbstractControl | null {
return this.userForm.get('firstName');
@@ -2,7 +2,6 @@ import { SelectionModel } from '@angular/cdk/collections';
import { Component, OnDestroy, OnInit } from '@angular/core';
import { MatButtonToggleChange } from '@angular/material/button-toggle';
import { MatTableDataSource } from '@angular/material/table';
import { ActivatedRoute, Params } from '@angular/router';
import { TranslateService } from '@ngx-translate/core';
import { Subscription } from 'rxjs';
import { ChangeType } from 'src/app/modules/changes/changes.component';
@@ -17,7 +16,6 @@ import { ToastService } from 'src/app/services/toast.service';
styleUrls: ['./org-detail.component.scss'],
})
export class OrgDetailComponent implements OnInit, OnDestroy {
public orgId: string = '';
public org!: Org.AsObject;
public dataSource: MatTableDataSource<OrgMember.AsObject> = new MatTableDataSource<OrgMember.AsObject>();
@@ -34,23 +32,20 @@ export class OrgDetailComponent implements OnInit, OnDestroy {
constructor(
public translate: TranslateService,
private route: ActivatedRoute,
private orgService: OrgService,
private toast: ToastService,
) { }
public ngOnInit(): void {
this.subscription = this.route.params.subscribe(params => this.getData(params));
this.getData();
}
public ngOnDestroy(): void {
this.subscription.unsubscribe();
}
private async getData({ id }: Params): Promise<void> {
this.orgId = id;
this.orgService.GetOrgById(this.orgId).then((org: Org) => {
private async getData(): Promise<void> {
this.orgService.GetMyOrg().then((org: Org) => {
this.org = org.toObject();
}).catch(error => {
this.toast.showError(error.message);
@@ -64,15 +59,15 @@ export class OrgDetailComponent implements OnInit, OnDestroy {
public changeState(event: MatButtonToggleChange | any): void {
if (event.value === OrgState.ORGSTATE_ACTIVE) {
this.orgService.ReactivateOrg(this.orgId).then(() => {
this.orgService.ReactivateMyOrg().then(() => {
this.toast.showInfo('Reactivated Org');
}).catch(error => {
}).catch((error) => {
this.toast.showError(error.message);
});
} else if (event.value === OrgState.ORGSTATE_INACTIVE) {
this.orgService.DeactivateOrg(this.orgId).then(() => {
this.orgService.DeactivateMyOrg().then(() => {
this.toast.showInfo('Deactivated Org');
}).catch(error => {
}).catch((error) => {
this.toast.showError(error.message);
});
}
@@ -88,6 +83,10 @@ export class OrgDetailComponent implements OnInit, OnDestroy {
console.log(domain);
this.orgService.RemoveMyOrgDomain(domain).then(() => {
this.toast.showInfo('Removed');
const index = this.domains.findIndex(d => d.domain === domain);
if (index > -1) {
this.domains.splice(index, 1);
}
}).catch(error => {
this.toast.showError(error.message);
});
@@ -16,6 +16,7 @@
[ngClass]="{ selected: selection.isSelected(org),active: activeOrg?.id === org?.id }">
<!-- <mat-icon matTooltip="select org" (click)="selection.toggle(org)" class="selection-icon">
check_circle</mat-icon> -->
<div class="text-part">
<!-- <span *ngIf="org?.changeDate" class="top">last modified on
{{
@@ -70,6 +70,7 @@ h1 {
}
.text-part {
position: relative;
flex: 1;
display: flex;
flex-direction: column;
@@ -1,7 +1,7 @@
<div class="max-width-container">
<div class="container">
<div class="left">
<a *ngIf="org" [routerLink]="[ '/orgs', org.id]" mat-icon-button>
<a *ngIf="org" [routerLink]="[ '/org']" mat-icon-button>
<mat-icon class="icon">arrow_back</mat-icon>
</a>
</div>
@@ -3,7 +3,6 @@ import { AfterViewInit, Component, ViewChild } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { MatPaginator } from '@angular/material/paginator';
import { MatTable } from '@angular/material/table';
import { ActivatedRoute } from '@angular/router';
import { tap } from 'rxjs/operators';
import { CreationType, MemberCreateDialogComponent } from 'src/app/modules/add-member-dialog/member-create-dialog.component';
import { Org, ProjectMember, ProjectType, User } from 'src/app/proto/generated/management_pb';
@@ -31,15 +30,12 @@ export class OrgMembersComponent implements AfterViewInit {
constructor(private orgService: OrgService,
private dialog: MatDialog,
private toast: ToastService,
private route: ActivatedRoute) {
this.route.params.subscribe(params => {
this.orgService.GetOrgById(params.orgid).then(org => {
this.org = org.toObject();
console.log(this.org);
this.dataSource = new ProjectMembersDataSource(this.orgService);
this.dataSource.loadMembers(0, 25, 'asc');
});
private toast: ToastService) {
this.orgService.GetMyOrg().then(org => {
this.org = org.toObject();
console.log(this.org);
this.dataSource = new ProjectMembersDataSource(this.orgService);
this.dataSource.loadMembers(0, 25, 'asc');
});
}
@@ -18,14 +18,14 @@ const routes: Routes = [
loadChildren: () => import('./org-create/org-create.module').then(m => m.OrgCreateModule),
},
{
path: ':id/policy/:policytype/create',
path: 'policy/:policytype/create',
component: PasswordPolicyComponent,
data: {
action: PolicyComponentAction.CREATE,
},
},
{
path: ':id/policy/:policytype',
path: 'policy/:policytype',
component: PasswordPolicyComponent,
data: {
action: PolicyComponentAction.MODIFY,
@@ -33,15 +33,15 @@ const routes: Routes = [
loadChildren: () => import('./password-policy/password-policy.module').then(m => m.PasswordPolicyModule),
},
{
path: ':orgid/members',
path: 'members',
loadChildren: () => import('./org-members/org-members.module').then(m => m.OrgMembersModule),
},
{
path: ':id',
path: '',
component: OrgDetailComponent,
},
{
path: '',
path: 'overview',
component: OrgGridComponent,
},
];
@@ -1,7 +1,7 @@
<div class="max-width-container">
<div class="container">
<div class="left">
<a [routerLink]="[ '/orgs/', orgId]" mat-icon-button>
<a [routerLink]="[ '/org']" mat-icon-button>
<mat-icon class="icon">arrow_back</mat-icon>
</a>
</div>
@@ -51,13 +51,13 @@
<span class="left-desc">{{'ORG.POLICY.DATA.MINLENGTH' | translate}}</span>
<span class="fill-space"></span>
<div class="length-wrapper">
<button mat-icon-button (click)="incrementLength()">
<mat-icon>add</mat-icon>
</button>
<span>{{complexityData?.minLength}}</span>
<button mat-icon-button (click)="decrementLength()">
<mat-icon>remove</mat-icon>
</button>
<span>{{complexityData?.minLength}}</span>
<button mat-icon-button (click)="incrementLength()">
<mat-icon>add</mat-icon>
</button>
</div>
</div>
<div class="row">
@@ -51,7 +51,7 @@ export class ProjectRoleCreateComponent implements OnInit, OnDestroy {
this.formGroup = new FormGroup({
key: new FormControl('', [Validators.required]),
displayName: new FormControl(''),
group: new FormControl('', [Validators.required]),
group: new FormControl(''),
});
this.formArray = new FormArray([this.formGroup]);
@@ -8,9 +8,7 @@
<div class="full-width">
<p class="desc">{{ 'PROJECT.PAGES.DESCRIPTION' | translate }}</p>
<p *ngIf="isZitadel" class="zitadel-warning">This belongs to Zitadel project. If you change something,
Zitadel
may not behave as intended!</p>
<p *ngIf="isZitadel" class="zitadel-warning">{{'PROJECT.PAGES.ZITADELPROJECT' | translate}}</p>
</div>
</div>
@@ -28,8 +28,6 @@
}}</span>
<span class="name" *ngIf="item.projectName">{{ item.projectName }}</span>
<span class="description" *ngIf="item.state">{{'PROJECT.STATE.'+item.state | translate}}</span>
<span class="description">{{'PROJECT.TYPE.TITLE' | translate}}:
{{'PROJECT.TYPE.'+ProjectType.PROJECTTYPE_GRANTED | translate}}</span>
<span *ngIf="item.changeDate" class="created">created on
{{
dateFromTimestamp(item.creationDate) | date: 'EEE dd. MMM, HH:mm'
@@ -55,4 +53,4 @@
</mat-menu>
</div>
<p class="n-items" *ngIf="!loading && items.length === 0">{{'PROJECT.PAGES.NOITEMS' | translate}}</p>
</div>
</div>
@@ -1,5 +1,5 @@
<app-granted-project-grid *ngIf="grid" [loading]="loading$ | async" (changedView)="grid = false"
[items]="grantedProjectList" (newClicked)="addProject()">
[items]="grantedProjectList">
</app-granted-project-grid>
<div *ngIf="!grid" class="view-toggle">
@@ -31,9 +31,6 @@
<mat-icon svgIcon="mdi_light_on"></mat-icon>
</button>
</div>
<a class="add-button" [routerLink]="[ '/projects', 'create']" color="primary" mat-raised-button>
<mat-icon class="icon">add</mat-icon>{{ 'ACTIONS.NEW' | translate }}
</a>
</div>
<div class="table-wrapper">
<div class="spinner-container" *ngIf="(loading$ | async) || (loading$ | async)">
@@ -77,13 +74,6 @@
*ngIf="project.state">{{'PROJECT.STATE.'+project.state | translate}}</span></td>
</ng-container>
<ng-container matColumnDef="type">
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.TABLE.TYPE' | translate }} </th>
<td mat-cell *matCellDef="let project">
<span *ngIf="project.type !== undefined">{{'PROJECT.TYPE.'+project.type | translate}}</span>
</td>
</ng-container>
<ng-container matColumnDef="creationDate">
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.TABLE.CREATIONDATE' | translate }} </th>
<td mat-cell *matCellDef="let project">
@@ -50,10 +50,6 @@
.icon-button {
margin-right: .5rem;
}
.add-button {
border-radius: .5rem;
}
}
.table-wrapper {
@@ -41,7 +41,7 @@ export class GrantedProjectListComponent implements OnInit, OnDestroy {
new MatTableDataSource<ProjectGrantView.AsObject>();
public grantedProjectList: ProjectGrantView.AsObject[] = [];
public displayedColumns: string[] = ['select', 'name', 'orgName', 'orgDomain', 'type', 'state', 'creationDate', 'changeDate'];
public displayedColumns: string[] = ['select', 'name', 'orgName', 'orgDomain', 'state', 'creationDate', 'changeDate'];
public selection: SelectionModel<ProjectGrantView.AsObject> = new SelectionModel<ProjectGrantView.AsObject>(true, []);
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
@@ -1,6 +1,6 @@
<app-meta-layout>
<div class="max-width-container">
<div class="head" *ngIf="project?.id">
<div class="head" *ngIf="project?.projectId">
<a [routerLink]="[ '/projects' ]" mat-icon-button>
<mat-icon class="icon">arrow_back</mat-icon>
</a>
@@ -39,7 +39,7 @@
<!-- show only on owned projects-->
<ng-container *ngIf="project">
<ng-template appHasRole [appHasRole]="['project.app.read:' + project.id, 'project.app.read']">
<ng-template appHasRole [appHasRole]="['project.app.read:' + project.projectId, 'project.app.read']">
<app-project-application-grid *ngIf="grid"
[disabled]="project?.state !== ProjectState.PROJECTSTATE_ACTIVE || isZitadel"
(changeView)="grid = false" [projectId]="projectId"></app-project-application-grid>
@@ -56,7 +56,8 @@
</ng-template>
<ng-container *ngIf="isZitadel == false">
<ng-template appHasRole [appHasRole]="['project.grant.read:' + project.id, 'project.grant.read']">
<ng-template appHasRole
[appHasRole]="['project.grant.read:' + project.projectId, 'project.grant.read']">
<app-card title="{{ 'PROJECT.GRANT.TITLE' | translate }}"
description="{{ 'PROJECT.GRANT.DESCRIPTION' | translate }}">
<app-project-grants [disabled]="project?.state !== ProjectState.PROJECTSTATE_ACTIVE"
@@ -65,7 +66,7 @@
</app-card>
</ng-template>
<ng-template appHasRole [appHasRole]="['project.role.read:' + project.id, 'project.role.read']">
<ng-template appHasRole [appHasRole]="['project.role.read:' + project.projectId, 'project.role.read']">
<app-card title="{{ 'PROJECT.ROLE.TITLE' | translate }}"
description="{{ 'PROJECT.ROLE.DESCRIPTION' | translate }}">
<app-project-roles [disabled]="project?.state !== ProjectState.PROJECTSTATE_ACTIVE"
@@ -97,7 +98,7 @@
</app-project-contributors>
</mat-tab>
<mat-tab label="{{ 'CHANGES.PROJECT.TITLE' | translate }}" class="flex-col">
<app-changes *ngIf="project" [changeType]="ChangeType.PROJECT" [id]="project.id"></app-changes>
<app-changes *ngIf="project" [changeType]="ChangeType.PROJECT" [id]="project.projectId"></app-changes>
</mat-tab>
</mat-tab-group>
</metainfo>
@@ -9,13 +9,13 @@ import { ChangeType } from 'src/app/modules/changes/changes.component';
import {
Application,
ApplicationSearchResponse,
Project,
ProjectMember,
ProjectMemberSearchResponse,
ProjectRole,
ProjectRoleSearchResponse,
ProjectState,
ProjectType,
ProjectView,
} from 'src/app/proto/generated/management_pb';
import { OrgService } from 'src/app/services/org.service';
import { ProjectService } from 'src/app/services/project.service';
@@ -29,7 +29,7 @@ import { ToastService } from 'src/app/services/toast.service';
})
export class OwnedProjectDetailComponent implements OnInit, OnDestroy {
public projectId: string = '';
public project!: Project.AsObject;
public project!: ProjectView.AsObject;
public pageSizeRoles: number = 10;
public roleDataSource: MatTableDataSource<ProjectRole.AsObject> = new MatTableDataSource<ProjectRole.AsObject>();
@@ -105,7 +105,7 @@ export class OwnedProjectDetailComponent implements OnInit, OnDestroy {
}
public saveProject(): void {
this.projectService.UpdateProject(this.project as Project.AsObject).then(() => {
this.projectService.UpdateProject(this.project.projectId, this.project.name).then(() => {
this.toast.showInfo('Project updated');
}).catch(error => {
this.toast.showInfo(error.message);
@@ -29,8 +29,6 @@
}}</span>
<span class="name" *ngIf="item.name">{{ item.name }}</span>
<span class="description" *ngIf="item.state">{{'PROJECT.STATE.'+item.state | translate}}</span>
<span class="description">{{'PROJECT.TYPE.TITLE' | translate}}:
{{'PROJECT.TYPE.'+ProjectType.PROJECTTYPE_OWNED | translate}}</span>
<span *ngIf="item.changeDate" class="created">created on
{{
dateFromTimestamp(item.creationDate) | date: 'EEE dd. MMM, HH:mm'
@@ -62,4 +60,4 @@
<mat-icon class="icon">add</mat-icon>
<span>Add new project</span>
</div>
</div>
</div>
@@ -58,30 +58,12 @@
<td mat-cell *matCellDef="let project"> {{project.name}} </td>
</ng-container>
<ng-container matColumnDef="orgName">
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.TABLE.ORGNAME' | translate }} </th>
<td class="pointer" mat-cell *matCellDef="let project">
{{project.orgName}} </td>
</ng-container>
<ng-container matColumnDef="orgDomain">
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.TABLE.ORGDOMAIN' | translate }} </th>
<td class="pointer" mat-cell *matCellDef="let project">
{{project?.orgDomain}} </td>
</ng-container>
<ng-container matColumnDef="state">
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.TABLE.STATE' | translate }} </th>
<td mat-cell *matCellDef="let project"><span
*ngIf="project.state">{{'PROJECT.STATE.'+project.state | translate}}</span></td>
</ng-container>
<ng-container matColumnDef="type">
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.TABLE.TYPE' | translate }} </th>
<td mat-cell *matCellDef="let project">
<span *ngIf="project.type !== undefined">{{'PROJECT.TYPE.'+project.type | translate}}</span>
</td>
</ng-container>
<ng-container matColumnDef="creationDate">
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.TABLE.CREATIONDATE' | translate }} </th>
@@ -108,4 +90,4 @@
<mat-paginator class="background-style" [length]="totalResult" [pageSize]="10" [pageSizeOptions]="[5, 10, 20]"
(page)="changePage($event)"></mat-paginator>
</div>
</div>
</div>
@@ -41,7 +41,7 @@ export class OwnedProjectListComponent implements OnInit, OnDestroy {
new MatTableDataSource<ProjectView.AsObject>();
public ownedProjectList: ProjectView.AsObject[] = [];
public displayedColumns: string[] = ['select', 'name', 'orgName', 'orgDomain', 'type', 'state', 'creationDate', 'changeDate'];
public displayedColumns: string[] = ['select', 'name', 'state', 'creationDate', 'changeDate'];
public selection: SelectionModel<ProjectView.AsObject> = new SelectionModel<ProjectView.AsObject>(true, []);
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
@@ -1,7 +1,7 @@
import { DataSource } from '@angular/cdk/collections';
import { BehaviorSubject, from, Observable, of } from 'rxjs';
import { catchError, finalize, map } from 'rxjs/operators';
import { Project, ProjectMember, ProjectMemberSearchResponse, ProjectType } from 'src/app/proto/generated/management_pb';
import { ProjectMember, ProjectMemberSearchResponse, ProjectType, ProjectView } from 'src/app/proto/generated/management_pb';
import { ProjectService } from 'src/app/services/project.service';
/**
@@ -19,7 +19,7 @@ export class ProjectMembersDataSource extends DataSource<ProjectMember.AsObject>
super();
}
public loadMembers(project: Project.AsObject,
public loadMembers(project: ProjectView.AsObject,
projectType: ProjectType,
pageIndex: number, pageSize: number, grantId?: string, sortDirection?: string): void {
const offset = pageIndex * pageSize;
@@ -28,9 +28,9 @@ export class ProjectMembersDataSource extends DataSource<ProjectMember.AsObject>
// TODO
const promise: Promise<ProjectMemberSearchResponse> | undefined =
projectType === ProjectType.PROJECTTYPE_OWNED ?
this.projectService.SearchProjectMembers(project.id, pageSize, offset) :
this.projectService.SearchProjectMembers(project.projectId, pageSize, offset) :
projectType === ProjectType.PROJECTTYPE_GRANTED && grantId ?
this.projectService.SearchProjectGrantMembers(project.id,
this.projectService.SearchProjectGrantMembers(project.projectId,
grantId, pageSize, offset) : undefined;
if (promise) {
from(promise).pipe(
@@ -1,7 +1,7 @@
<div class="max-width-container">
<div class="container">
<div class="left">
<a *ngIf="project" [routerLink]="[ '/projects', project.id]" mat-icon-button>
<a *ngIf="project" [routerLink]="[ '/projects', project.projectId]" mat-icon-button>
<mat-icon class="icon">arrow_back</mat-icon>
</a>
</div>
@@ -23,14 +23,16 @@
</ng-container>
</div>
<span class="fill-space"></span>
<ng-template appHasRole [appHasRole]="['project.member.delete:'+project.id,'project.member.delete']">
<ng-template appHasRole
[appHasRole]="['project.member.delete:' + project.projectId, 'project.member.delete']">
<button (click)="removeProjectMemberSelection()"
matTooltip="{{'ORG_DETAIL.TABLE.DELETE' | translate}}" class="icon-button" mat-icon-button
*ngIf="selection.hasValue()">
<mat-icon>remove_circle</mat-icon>
</button>
</ng-template>
<ng-template appHasRole [appHasRole]="['project.member.write:'+project.id,'project.member.write']">
<ng-template appHasRole
[appHasRole]="['project.member.write:'+project.projectId,'project.member.write']">
<a color="primary" [disabled]="disabled" class="add-button" (click)="openAddMember()"
color="primary" mat-raised-button>
<mat-icon class="icon">add</mat-icon>{{ 'ACTIONS.NEW' | translate }}
@@ -4,7 +4,7 @@ import { MatPaginator } from '@angular/material/paginator';
import { MatTable } from '@angular/material/table';
import { ActivatedRoute } from '@angular/router';
import { tap } from 'rxjs/operators';
import { Project, ProjectMember, ProjectType } from 'src/app/proto/generated/management_pb';
import { ProjectMember, ProjectType, ProjectView } from 'src/app/proto/generated/management_pb';
import { ProjectService } from 'src/app/services/project.service';
import { ToastService } from 'src/app/services/toast.service';
@@ -16,7 +16,7 @@ import { ProjectMembersDataSource } from './project-members-datasource';
styleUrls: ['./project-members.component.scss'],
})
export class ProjectMembersComponent implements AfterViewInit {
public project!: Project.AsObject;
public project!: ProjectView.AsObject;
public projectType: ProjectType = ProjectType.PROJECTTYPE_OWNED;
public disabled: boolean = false;
@ViewChild(MatPaginator) public paginator!: MatPaginator;
@@ -60,7 +60,7 @@ export class ProjectMembersComponent implements AfterViewInit {
public removeProjectMemberSelection(): void {
Promise.all(this.selection.selected.map(member => {
return this.projectService.RemoveProjectMember(this.project.id, member.userId).then(() => {
return this.projectService.RemoveProjectMember(this.project.projectId, member.userId).then(() => {
this.toast.showInfo('Removed successfully');
}).catch(error => {
this.toast.showError(error.message);
@@ -69,7 +69,7 @@ export class ProjectMembersComponent implements AfterViewInit {
}
public removeMember(member: ProjectMember.AsObject): void {
this.projectService.RemoveProjectMember(this.project.id, member.userId).then(() => {
this.projectService.RemoveProjectMember(this.project.projectId, member.userId).then(() => {
this.toast.showInfo('Member removed successfully');
}).catch(error => {
this.toast.showError(error.message);
@@ -12,6 +12,7 @@ import { MatSortModule } from '@angular/material/sort';
import { MatTableModule } from '@angular/material/table';
import { MatTooltipModule } from '@angular/material/tooltip';
import { TranslateModule } from '@ngx-translate/core';
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
import { ProjectMembersRoutingModule } from './project-members-routing.module';
import { ProjectMembersComponent } from './project-members.component';
@@ -23,6 +24,7 @@ import { ProjectMembersComponent } from './project-members.component';
ProjectMembersRoutingModule,
CommonModule,
MatAutocompleteModule,
HasRoleModule,
MatChipsModule,
MatButtonModule,
MatCheckboxModule,
@@ -4,12 +4,25 @@
<mat-progress-bar *ngIf="loading" color="accent" mode="indeterminate"></mat-progress-bar>
<span *ngIf="!loading && !profile">No user</span>
<span *ngIf="!loading && !user">{{ 'USER.PAGES.NOUSER' | translate }}</span>
<div class="col" *ngIf="profile">
<app-card title="{{ 'USER.PAGES.LOGINNAMES' | translate }}"
description="{{ 'USER.PAGES.LOGINNAMESDESC' | translate }}" *ngIf="user">
<div class="login-name-row" *ngFor="let login of user?.loginNamesList">
<span>{{login}}</span>
<button color="primary" [disabled]="copied == login"
[matTooltip]="(copied != login ? 'USER.PAGES.COPY' : 'USER.PAGES.COPIED' ) | translate"
(click)="copytoclipboard(login)" mat-icon-button>
<i *ngIf="copied != login" class="las la-clipboard"></i>
<i *ngIf="copied == login" class="las la-clipboard-check"></i>
</button>
</div>
</app-card>
<div class="col" *ngIf="user">
<app-card title="{{ 'USER.PROFILE.TITLE' | translate }}"
description="{{'USER.PROFILE.DESCRIPTION' | translate}}">
<app-detail-form [genders]="genders" [languages]="languages" [profile]="profile"
<app-detail-form [genders]="genders" [languages]="languages" [profile]="user"
(changedLanguage)="changedLanguage($event)" (submitData)="saveProfile($event)">
</app-detail-form>
</app-card>
@@ -19,7 +32,7 @@
</app-card>
</div>
<app-card *ngIf="profile" title="{{'USER.PASSWORD.TITLE' | translate}}"
<app-card *ngIf="user" title="{{'USER.PASSWORD.TITLE' | translate}}"
description="{{'USER.PASSWORD.DESCRIPTION' | translate}}">
<form *ngIf="passwordForm" [formGroup]="passwordForm" (ngSubmit)="setPassword()">
<div class="content">
@@ -65,7 +78,7 @@
</form>
</app-card>
<app-card *ngIf="profile" title="{{ 'USER.LOGINMETHODS.TITLE' | translate }}"
<app-card *ngIf="user" title="{{ 'USER.LOGINMETHODS.TITLE' | translate }}"
description="{{ 'USER.LOGINMETHODS.DESCRIPTION' | translate }}">
<div class="method-col">
<div class="method-row">
@@ -148,9 +161,9 @@
</div>
</app-card>
<app-auth-user-mfa *ngIf="profile" [profile]="profile"></app-auth-user-mfa>
<app-auth-user-mfa *ngIf="user"></app-auth-user-mfa>
<app-card title="{{ 'USER.ADDRESS.TITLE' | translate }}" *ngIf="profile">
<app-card title="{{ 'USER.ADDRESS.TITLE' | translate }}" *ngIf="user">
<form [formGroup]="addressForm" (ngSubmit)="saveAddress()">
<div class="content">
<mat-form-field class="formfield">
@@ -29,6 +29,30 @@ h1 {
color: #81868a;
margin-bottom: 2rem;
}
.login-name-row {
display: flex;
align-items: center;
button {
transition: opacity .15s ease-in-out;
visibility: hidden;
opacity: 0;
&[disabled] {
visibility: visible;
color: white;
opacity: 1;
}
}
&:hover {
button {
visibility: visible;
opacity: 1;
}
}
}
.content {
display: flex;
@@ -3,10 +3,9 @@ import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/fo
import { MatDialog } from '@angular/material/dialog';
import { TranslateService } from '@ngx-translate/core';
import { Subscription } from 'rxjs';
import { Gender, UserAddress, UserEmail, UserPhone, UserProfile } from 'src/app/proto/generated/auth_pb';
import { Gender, UserAddress, UserEmail, UserPhone, UserProfile, UserView } from 'src/app/proto/generated/auth_pb';
import { PasswordComplexityPolicy } from 'src/app/proto/generated/management_pb';
import { AuthUserService } from 'src/app/services/auth-user.service';
import { MgmtUserService } from 'src/app/services/mgmt-user.service';
import { OrgService } from 'src/app/services/org.service';
import { ToastService } from 'src/app/services/toast.service';
@@ -33,7 +32,7 @@ function passwordConfirmValidator(c: AbstractControl): any {
styleUrls: ['./auth-user-detail.component.scss'],
})
export class AuthUserDetailComponent implements OnDestroy {
public profile!: UserProfile.AsObject;
public user!: UserView.AsObject;
public email: UserEmail.AsObject = { email: '' } as any;
public phone: UserPhone.AsObject = { phone: '' } as any;
public address: UserAddress.AsObject = { id: '' } as any;
@@ -50,11 +49,11 @@ export class AuthUserDetailComponent implements OnDestroy {
public loading: boolean = false;
public policy!: PasswordComplexityPolicy.AsObject;
public copied: string = '';
constructor(
public translate: TranslateService,
private toast: ToastService,
private mgmtUserService: MgmtUserService,
private userService: AuthUserService,
private fb: FormBuilder,
private dialog: MatDialog,
@@ -63,6 +62,7 @@ export class AuthUserDetailComponent implements OnDestroy {
const validators: Validators[] = [Validators.required];
this.orgService.GetPasswordComplexityPolicy().then(data => {
this.policy = data.toObject();
console.log(this.policy);
if (this.policy.minLength) {
validators.push(Validators.minLength(this.policy.minLength));
}
@@ -76,8 +76,7 @@ export class AuthUserDetailComponent implements OnDestroy {
validators.push(Validators.pattern(/[0-9]/g));
}
if (this.policy.hasSymbol) {
// All characters that are not a digit or an English letter \W or a whitespace \S
validators.push(Validators.pattern(/[\W\S]/));
validators.push(Validators.pattern(/[^a-z0-9]/gi));
}
this.passwordForm = this.fb.group({
@@ -86,11 +85,12 @@ export class AuthUserDetailComponent implements OnDestroy {
confirmPassword: ['', [...validators, passwordConfirmValidator]],
});
}).catch(error => {
console.log('no password complexity policy defined!');
this.toast.showError(error.message);
console.error(error.message);
this.passwordForm = this.fb.group({
currentPassword: ['', []],
newPassword: ['', []],
confirmPassword: ['', [passwordConfirmValidator]],
newPassword: ['', validators],
confirmPassword: ['', [...validators, passwordConfirmValidator]],
});
});
@@ -116,18 +116,24 @@ export class AuthUserDetailComponent implements OnDestroy {
public saveProfile(profileData: UserProfile.AsObject): void {
console.log(profileData);
this.profile.firstName = profileData.firstName;
this.profile.lastName = profileData.lastName;
this.profile.nickName = profileData.nickName;
this.profile.displayName = profileData.displayName;
this.profile.gender = profileData.gender;
this.profile.preferredLanguage = profileData.preferredLanguage;
console.log(this.profile);
this.user.firstName = profileData.firstName;
this.user.lastName = profileData.lastName;
this.user.nickName = profileData.nickName;
this.user.displayName = profileData.displayName;
this.user.gender = profileData.gender;
this.user.preferredLanguage = profileData.preferredLanguage;
console.log(this.user);
this.userService
.SaveMyUserProfile(this.profile as UserProfile.AsObject)
.SaveMyUserProfile(
this.user.firstName,
this.user.lastName,
this.user.nickName,
this.user.preferredLanguage,
this.user.gender,
)
.then((data: UserProfile) => {
this.toast.showInfo('Saved Profile');
this.profile = data.toObject();
this.user = Object.assign(this.user, data.toObject());
})
.catch(data => {
this.toast.showError(data.message);
@@ -214,7 +220,7 @@ export class AuthUserDetailComponent implements OnDestroy {
public savePhone(): void {
this.phoneEditState = false;
if (!this.phone.id) {
this.phone.id = this.profile.id;
this.phone.id = this.user.id;
}
this.userService
.SaveMyUserPhone(this.phone).then((data: UserPhone) => {
@@ -265,12 +271,35 @@ export class AuthUserDetailComponent implements OnDestroy {
}
private async getData(): Promise<void> {
this.profile = (await this.userService.GetMyUserProfile()).toObject();
this.userService.GetMyUser().then(user => {
console.log(user.toObject());
this.user = user.toObject();
}).catch(err => {
console.error(err);
});
this.email = (await this.userService.GetMyUserEmail()).toObject();
this.phone = (await this.userService.GetMyUserPhone()).toObject();
this.address = (await this.userService.GetMyUserAddress()).toObject();
console.log(this.profile);
this.addressForm.patchValue(this.address);
}
public copytoclipboard(value: string): void {
const selBox = document.createElement('textarea');
selBox.style.position = 'fixed';
selBox.style.left = '0';
selBox.style.top = '0';
selBox.style.opacity = '0';
selBox.value = value;
document.body.appendChild(selBox);
selBox.focus();
selBox.select();
document.execCommand('copy');
document.body.removeChild(selBox);
this.copied = value;
setTimeout(() => {
this.copied = '';
}, 3000);
}
}
@@ -1,10 +1,8 @@
import { Component, Input, OnDestroy, OnInit } from '@angular/core';
import { Component, OnDestroy, OnInit } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { BehaviorSubject, Observable } from 'rxjs';
import { MfaOtpResponse, MFAState, MfaType, MultiFactor } from 'src/app/proto/generated/auth_pb';
import { UserProfile } from 'src/app/proto/generated/management_pb';
import { AuthUserService } from 'src/app/services/auth-user.service';
import { MgmtUserService } from 'src/app/services/mgmt-user.service';
import { ToastService } from 'src/app/services/toast.service';
import { DialogOtpComponent } from '../dialog-otp/dialog-otp.component';
@@ -15,18 +13,15 @@ import { DialogOtpComponent } from '../dialog-otp/dialog-otp.component';
styleUrls: ['./auth-user-mfa.component.scss'],
})
export class AuthUserMfaComponent implements OnInit, OnDestroy {
@Input() private profile!: UserProfile.AsObject;
public mfaSubject: BehaviorSubject<MultiFactor.AsObject[]> = new BehaviorSubject<MultiFactor.AsObject[]>([]);
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
public loading$: Observable<boolean> = this.loadingSubject.asObservable();
public MfaType: any = MfaType;
public MFAState: any = MFAState;
constructor(private userService: AuthUserService, private mgmtUserService: MgmtUserService,
private toast: ToastService, private dialog: MatDialog) { }
constructor(private userService: AuthUserService, private toast: ToastService, private dialog: MatDialog) { }
public ngOnInit(): void {
console.log(this.profile);
this.getOTP();
}
@@ -58,8 +53,7 @@ export class AuthUserMfaComponent implements OnInit, OnDestroy {
}
public getOTP(): void {
console.log('otp');
this.mgmtUserService.getUserMfas(this.profile.id).then(mfas => {
this.userService.GetMyMfas().then(mfas => {
this.mfaSubject.next(mfas.toObject().mfasList);
console.log(mfas.toObject());
}).catch(error => {
@@ -12,6 +12,8 @@
<mat-progress-bar *ngIf="loading" color="accent" mode="indeterminate"></mat-progress-bar>
<span *ngIf="!loading && !user">{{ 'USER.PAGES.NOUSER' | translate }}</span>
<ng-template appHasRole [appHasRole]="['user.read', 'user.read:'+user?.id]">
<app-card title="{{ 'USER.PROFILE.TITLE' | translate }}"
description="{{'USER.PROFILE.DESCRIPTION' | translate}}">
@@ -149,7 +151,7 @@
</div>
</app-card>
<app-auth-user-mfa *ngIf="user" [profile]="user"></app-auth-user-mfa>
<app-auth-user-mfa *ngIf="user" [user]="user"></app-auth-user-mfa>
<app-card title="{{ 'USER.ADDRESS.TITLE' | translate }}">
<form [formGroup]="addressForm" (ngSubmit)="saveAddress()">
@@ -93,8 +93,7 @@ export class UserDetailComponent implements OnInit, OnDestroy {
validators.push(Validators.pattern(/[0-9]/g));
}
if (this.policy.hasSymbol) {
// All characters that are not a digit or an English letter \W or a whitespace \S
validators.push(Validators.pattern(/[\W\S]/));
validators.push(Validators.pattern(/[^a-z0-9]/gi));
}
this.passwordForm = this.fb.group({
@@ -161,7 +160,13 @@ export class UserDetailComponent implements OnInit, OnDestroy {
this.user.preferredLanguage = profileData.preferredLanguage;
console.log(this.user);
this.mgmtUserService
.SaveUserProfile(this.user)
.SaveUserProfile(
this.user.id,
this.user.firstName,
this.user.lastName,
this.user.nickName,
this.user.preferredLanguage,
this.user.gender)
.then((data: UserProfile) => {
this.toast.showInfo('Saved Profile');
this.user = Object.assign(this.user, data.toObject());
@@ -1,11 +1,13 @@
<app-card title="{{'USER.MFA.TITLE' | translate}}" description="{{'USER.MFA.MANAGE_DESCRIPTION' | translate}}">
<app-card title="{{'USER.MFA.TITLE' | translate}}" description="{{'USER.MFA.DESCRIPTION' | translate}}">
<div class="col">
<div class="row" *ngFor="let mfa of mfaSubject | async">
<span>{{'USER.MFA.TYPE.'+ mfa.type | translate}}</span>
<span>{{'USER.MFA.STATE.'+ mfa.state | translate}}</span>
</div>
</div>
<div class="table-wrapper">
<div class="spinner-container" *ngIf="loading$ | async">
<mat-spinner diameter="50"></mat-spinner>
</div>
<div class="col">
<div class="row"></div>
</div>
</div>
</app-card>
@@ -1,80 +1,18 @@
.table-header-row {
.col {
display: flex;
align-items: center;
.col {
display: flex;
flex-direction: column;
.desc {
font-size: .8rem;
color: #81868a;
}
.count {
font-size: 2rem;
}
}
.fill-space {
flex: 1;
}
.icon-button {
margin-right: .5rem;
}
.add-button {
border-radius: .5rem;
}
}
.table-wrapper {
overflow: auto;
flex-direction: column;
border-bottom: 1px solid #ffffff20;
margin-bottom: 1rem;
.spinner-container {
.row {
display: flex;
justify-content: space-between;
padding: .5rem 0;
align-items: center;
justify-content: center;
}
table, mat-paginator {
width: 100%;
td, th {
&:first-child {
padding-left: 0;
padding-right: 1rem;
}
&:last-child {
padding-right: 0;
}
}
.data-row {
cursor: pointer;
&:hover {
background-color: #ffffff05;
}
}
.selection {
width: 50px;
max-width: 50px;
}
}
}
.add-row {
display: flex;
margin: -.5rem;
button {
border-radius: .5rem;
margin: .5rem;
mat-icon {
margin-right: .5rem;
span {
flex: 1;
}
}
}
@@ -1,6 +1,8 @@
import { Component, Input, OnInit } from '@angular/core';
import { Component, Input, OnDestroy, OnInit } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
import { UserProfile } from 'src/app/proto/generated/management_pb';
import { MFAState, MfaType, MultiFactor, UserView } from 'src/app/proto/generated/management_pb';
import { MgmtUserService } from 'src/app/services/mgmt-user.service';
import { ToastService } from 'src/app/services/toast.service';
export interface MFAItem {
@@ -13,16 +15,53 @@ export interface MFAItem {
templateUrl: './user-mfa.component.html',
styleUrls: ['./user-mfa.component.scss'],
})
export class UserMfaComponent implements OnInit {
@Input() public profile!: UserProfile;
public mfaSubject: BehaviorSubject<MFAItem[]> = new BehaviorSubject<MFAItem[]>([]);
export class UserMfaComponent implements OnInit, OnDestroy {
@Input() private user!: UserView.AsObject;
public mfaSubject: BehaviorSubject<MultiFactor.AsObject[]> = new BehaviorSubject<MultiFactor.AsObject[]>([]);
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
public loading$: Observable<boolean> = this.loadingSubject.asObservable();
constructor() { }
ngOnInit(): void {
public MfaType: any = MfaType;
public MFAState: any = MFAState;
constructor(private mgmtUserService: MgmtUserService,
private toast: ToastService) { }
public ngOnInit(): void {
console.log(this.user);
this.getOTP();
}
public ngOnDestroy(): void {
this.mfaSubject.complete();
this.loadingSubject.complete();
}
public getOTP(): void {
console.log('otp', this.user);
this.mgmtUserService.getUserMfas(this.user.id).then(mfas => {
this.mfaSubject.next(mfas.toObject().mfasList);
console.log(mfas.toObject());
}).catch(error => {
console.error(error);
this.toast.showError(error.message);
});
}
// public deleteMFA(type: MfaType): void {
// if (type === MfaType.MFATYPE_OTP) {
// this.userService.RemoveMfaOTP().then(() => {
// this.toast.showInfo('OTP Deleted');
// const index = this.mfaSubject.value.findIndex(mfa => mfa.type === type);
// if (index > -1) {
// const newValues = this.mfaSubject.value;
// newValues.splice(index, 1);
// this.mfaSubject.next(newValues);
// }
// }).catch(error => {
// this.toast.showError(error.message);
// });
// }
// }
}