chore(console): resolve warnings due to dependency update (#4270)

* cli, core

* material cdk

* schematics

* chore(deps): bump ngx-color from 7.3.3 to 8.0.2 in /console (#4228)

Bumps [ngx-color](https://github.com/scttcper/ngx-color) from 7.3.3 to 8.0.2.
- [Release notes](https://github.com/scttcper/ngx-color/releases)
- [Commits](https://github.com/scttcper/ngx-color/compare/v7.3.3...v8.0.2)

---
updated-dependencies:
- dependency-name: ngx-color
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

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

* eslint-plugin

* chore(deps): bump moment from 2.29.3 to 2.29.4 in /console (#3926)

Bumps [moment](https://github.com/moment/moment) from 2.29.3 to 2.29.4.
- [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.29.3...2.29.4)

---
updated-dependencies:
- dependency-name: moment
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

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

* chore(deps): bump codemirror from 5.65.6 to 6.0.1 in /console (#3928)

Bumps [codemirror](https://github.com/codemirror/basic-setup) from 5.65.6 to 6.0.1.
- [Release notes](https://github.com/codemirror/basic-setup/releases)
- [Changelog](https://github.com/codemirror/basic-setup/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codemirror/basic-setup/commits/6.0.1)

---
updated-dependencies:
- dependency-name: codemirror
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

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

* lock

* use codemirror 5

* remove redundant null undefined checks

* i18n, undefined checks

* remove redundant null and undefined checks

* checks

* fix: resolve null check warnings

* commonjs deps

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Livio Spring <livio.a@gmail.com>
This commit is contained in:
Max Peintner
2022-09-01 09:44:39 +02:00
committed by GitHub
parent 2f647ce9a2
commit 861464598a
62 changed files with 927 additions and 780 deletions

View File

@@ -22,18 +22,14 @@
"main": "src/main.ts", "main": "src/main.ts",
"polyfills": "src/polyfills.ts", "polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json", "tsConfig": "tsconfig.app.json",
"assets": [ "assets": ["src/favicon.ico", "src/assets", "src/manifest.webmanifest"],
"src/favicon.ico", "styles": ["src/styles.scss"],
"src/assets", "scripts": ["./node_modules/tinycolor2/dist/tinycolor-min.js"],
"src/manifest.webmanifest"
],
"styles": [
"src/styles.scss"
],
"scripts": [
"./node_modules/tinycolor2/dist/tinycolor-min.js"
],
"allowedCommonJsDependencies": [ "allowedCommonJsDependencies": [
"fast-sha256",
"buffer",
"moment",
"grpc-web",
"@angular/common/locales/de", "@angular/common/locales/de",
"codemirror/mode/javascript/javascript", "codemirror/mode/javascript/javascript",
"src/app/proto/generated/zitadel/admin_pb", "src/app/proto/generated/zitadel/admin_pb",
@@ -130,27 +126,15 @@
"polyfills": "src/polyfills.ts", "polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.spec.json", "tsConfig": "tsconfig.spec.json",
"karmaConfig": "karma.conf.js", "karmaConfig": "karma.conf.js",
"assets": [ "assets": ["src/favicon.ico", "src/assets", "src/manifest.webmanifest"],
"src/favicon.ico", "styles": ["./node_modules/@angular/material/prebuilt-themes/pink-bluegrey.css", "src/styles.scss"],
"src/assets", "scripts": ["./node_modules/tinycolor2/dist/tinycolor-min.js"]
"src/manifest.webmanifest"
],
"styles": [
"./node_modules/@angular/material/prebuilt-themes/pink-bluegrey.css",
"src/styles.scss"
],
"scripts": [
"./node_modules/tinycolor2/dist/tinycolor-min.js"
]
} }
}, },
"lint": { "lint": {
"builder": "@angular-eslint/builder:lint", "builder": "@angular-eslint/builder:lint",
"options": { "options": {
"lintFilePatterns": [ "lintFilePatterns": ["src/**/*.ts", "src/**/*.html"]
"src/**/*.ts",
"src/**/*.html"
]
} }
}, },
"e2e": { "e2e": {
@@ -173,8 +157,6 @@
}, },
"cli": { "cli": {
"analytics": "2b4e8e6c-f053-4562-b7a6-00c6c06a6791", "analytics": "2b4e8e6c-f053-4562-b7a6-00c6c06a6791",
"schematicCollections": [ "schematicCollections": ["@angular-eslint/schematics"]
"@angular-eslint/schematics"
]
} }
} }

View File

@@ -1,13 +1,13 @@
<div class="accounts-card"> <div class="accounts-card">
<cnsl-avatar <cnsl-avatar
(click)="editUserProfile()" (click)="editUserProfile()"
*ngIf="user.human?.profile && user.human?.profile?.displayName" *ngIf="user && user.human?.profile && user.human?.profile?.displayName"
class="avatar" class="avatar"
[ngClass]="{ 'iam-user': iamuser }" [ngClass]="{ 'iam-user': iamuser }"
[forColor]="user.preferredLoginName" [forColor]="user.preferredLoginName"
[avatarUrl]="user.human?.profile?.avatarUrl || ''" [avatarUrl]="user.human?.profile?.avatarUrl || ''"
[name]=" [name]="
user.human && user.human.profile && user.human.profile?.displayName user.human && user.human.profile && user.human.profile.displayName
? user.human.profile.displayName ? user.human.profile.displayName
: user.human?.profile?.firstName + ' ' + user.human?.profile?.lastName : user.human?.profile?.firstName + ' ' + user.human?.profile?.lastName
" "
@@ -15,8 +15,8 @@
> >
</cnsl-avatar> </cnsl-avatar>
<span class="u-name">{{ user.human?.profile?.displayName ? user.human?.profile?.displayName : 'A' }}</span> <span class="u-name">{{ user?.human?.profile?.displayName ? user?.human?.profile?.displayName : 'A' }}</span>
<span class="u-email" *ngIf="user.preferredLoginName">{{ user.preferredLoginName }}</span> <span class="u-email" *ngIf="user?.preferredLoginName">{{ user?.preferredLoginName }}</span>
<button (click)="editUserProfile()" mat-stroked-button>{{ 'USER.EDITACCOUNT' | translate }}</button> <button (click)="editUserProfile()" mat-stroked-button>{{ 'USER.EDITACCOUNT' | translate }}</button>
<div class="l-accounts"> <div class="l-accounts">

View File

@@ -11,7 +11,7 @@ import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
styleUrls: ['./accounts-card.component.scss'], styleUrls: ['./accounts-card.component.scss'],
}) })
export class AccountsCardComponent implements OnInit { export class AccountsCardComponent implements OnInit {
@Input() public user!: User.AsObject; @Input() public user?: User.AsObject;
@Input() public iamuser: boolean | null = false; @Input() public iamuser: boolean | null = false;
@Output() public closedCard: EventEmitter<void> = new EventEmitter(); @Output() public closedCard: EventEmitter<void> = new EventEmitter();
@@ -22,12 +22,7 @@ export class AccountsCardComponent implements OnInit {
this.userService this.userService
.listMyUserSessions() .listMyUserSessions()
.then((sessions) => { .then((sessions) => {
this.sessions = sessions.resultList; this.sessions = sessions.resultList.filter((user) => user.loginName !== this.user?.preferredLoginName);
const index = this.sessions.findIndex((user) => user.loginName === this.user.preferredLoginName);
if (index > -1) {
this.sessions.splice(index, 1);
}
this.loadingUsers = false; this.loadingUsers = false;
}) })
.catch(() => { .catch(() => {

View File

@@ -18,7 +18,7 @@
<mat-datepicker #picker [startAt]="startDate"></mat-datepicker> <mat-datepicker #picker [startAt]="startDate"></mat-datepicker>
<span cnslError *ngIf="dateControl?.errors?.matDatepickerMin?.min"> <span cnslError *ngIf="dateControl?.errors?.matDatepickerMin?.min">
{{ 'USER.MACHINE.CHOOSEDATEAFTER' | translate }}: {{ 'USER.MACHINE.CHOOSEDATEAFTER' | translate }}:
{{ dateControl?.errors?.matDatepickerMin.min.toDate() | localizedDate: 'EEE dd. MMM' }} {{ dateControl.errors?.matDatepickerMin.min.toDate() | localizedDate: 'EEE dd. MMM' }}
</span> </span>
</cnsl-form-field> </cnsl-form-field>
</div> </div>

View File

@@ -9,7 +9,7 @@
<mat-datepicker #picker startView="year" [startAt]="startDate"></mat-datepicker> <mat-datepicker #picker startView="year" [startAt]="startDate"></mat-datepicker>
<span cnslError *ngIf="dateControl?.errors?.matDatepickerMin?.min"> <span cnslError *ngIf="dateControl?.errors?.matDatepickerMin?.min">
{{ 'USER.PERSONALACCESSTOKEN.ADD.CHOOSEDATEAFTER' | translate }}: {{ 'USER.PERSONALACCESSTOKEN.ADD.CHOOSEDATEAFTER' | translate }}:
{{ dateControl?.errors?.matDatepickerMin.min.toDate() | localizedDate: 'EEE dd. MMM' }} {{ dateControl.errors?.matDatepickerMin.min.toDate() | localizedDate: 'EEE dd. MMM' }}
</span> </span>
</cnsl-form-field> </cnsl-form-field>
</div> </div>

View File

@@ -27,15 +27,19 @@ export class ClientKeysComponent implements OnInit {
@ViewChild(PaginatorComponent) public paginator!: PaginatorComponent; @ViewChild(PaginatorComponent) public paginator!: PaginatorComponent;
public dataSource: MatTableDataSource<Key.AsObject> = new MatTableDataSource<Key.AsObject>(); public dataSource: MatTableDataSource<Key.AsObject> = new MatTableDataSource<Key.AsObject>();
public selection: SelectionModel<Key.AsObject> = new SelectionModel<Key.AsObject>(true, []); public selection: SelectionModel<Key.AsObject> = new SelectionModel<Key.AsObject>(true, []);
public keyResult!: ListAppKeysResponse.AsObject; public keyResult?: ListAppKeysResponse.AsObject;
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false); private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
public loading$: Observable<boolean> = this.loadingSubject.asObservable(); public loading$: Observable<boolean> = this.loadingSubject.asObservable();
@Input() public displayedColumns: string[] = ['select', 'id', 'type', 'creationDate', 'expirationDate', 'actions']; @Input() public displayedColumns: string[] = ['select', 'id', 'type', 'creationDate', 'expirationDate', 'actions'];
@Output() public changedSelection: EventEmitter<Array<Key.AsObject>> = new EventEmitter(); @Output() public changedSelection: EventEmitter<Array<Key.AsObject>> = new EventEmitter();
constructor(public translate: TranslateService, private mgmtService: ManagementService, private dialog: MatDialog, constructor(
private toast: ToastService) { public translate: TranslateService,
private mgmtService: ManagementService,
private dialog: MatDialog,
private toast: ToastService,
) {
this.selection.changed.subscribe(() => { this.selection.changed.subscribe(() => {
this.changedSelection.emit(this.selection.selected); this.changedSelection.emit(this.selection.selected);
}); });
@@ -45,7 +49,6 @@ export class ClientKeysComponent implements OnInit {
this.getData(10, 0); this.getData(10, 0);
} }
public isAllSelected(): boolean { public isAllSelected(): boolean {
const numSelected = this.selection.selected.length; const numSelected = this.selection.selected.length;
const numRows = this.dataSource.data.length; const numRows = this.dataSource.data.length;
@@ -53,22 +56,22 @@ export class ClientKeysComponent implements OnInit {
} }
public masterToggle(): void { public masterToggle(): void {
this.isAllSelected() ? this.isAllSelected() ? this.selection.clear() : this.dataSource.data.forEach((row) => this.selection.select(row));
this.selection.clear() :
this.dataSource.data.forEach(row => this.selection.select(row));
} }
public changePage(event: PageEvent): void { public changePage(event: PageEvent): void {
this.getData(event.pageSize, event.pageIndex * event.pageSize); this.getData(event.pageSize, event.pageIndex * event.pageSize);
} }
public deleteKey(key: Key.AsObject): void { public deleteKey(key: Key.AsObject): void {
this.mgmtService.removeAppKey(this.projectId, this.appId, key.id).then(() => { this.mgmtService
.removeAppKey(this.projectId, this.appId, key.id)
.then(() => {
this.selection.clear(); this.selection.clear();
this.toast.showInfo('USER.TOAST.SELECTEDKEYSDELETED', true); this.toast.showInfo('USER.TOAST.SELECTEDKEYSDELETED', true);
this.getData(10, 0); this.getData(10, 0);
}).catch(error => { })
.catch((error) => {
this.toast.showError(error); this.toast.showError(error);
}); });
} }
@@ -96,12 +99,9 @@ export class ClientKeysComponent implements OnInit {
} }
if (type) { if (type) {
this.mgmtService.addAppKey( this.mgmtService
this.projectId, .addAppKey(this.projectId, this.appId, type, date ? date : undefined)
this.appId, .then((response) => {
type,
date ? date : undefined,
).then((response) => {
if (response) { if (response) {
setTimeout(() => { setTimeout(() => {
this.refreshPage(); this.refreshPage();
@@ -115,7 +115,8 @@ export class ClientKeysComponent implements OnInit {
width: '400px', width: '400px',
}); });
} }
}).catch((error: any) => { })
.catch((error: any) => {
this.toast.showError(error); this.toast.showError(error);
}); });
} }
@@ -126,11 +127,14 @@ export class ClientKeysComponent implements OnInit {
private async getData(limit: number, offset: number): Promise<void> { private async getData(limit: number, offset: number): Promise<void> {
this.loadingSubject.next(true); this.loadingSubject.next(true);
if (this.projectId && this.appId) { if (this.projectId && this.appId) {
this.mgmtService.listAppKeys(this.projectId, this.appId, limit, offset).then(resp => { this.mgmtService
.listAppKeys(this.projectId, this.appId, limit, offset)
.then((resp) => {
this.keyResult = resp; this.keyResult = resp;
this.dataSource.data = this.keyResult.resultList; this.dataSource.data = this.keyResult.resultList;
this.loadingSubject.next(false); this.loadingSubject.next(false);
}).catch((error: any) => { })
.catch((error: any) => {
this.toast.showError(error); this.toast.showError(error);
this.loadingSubject.next(false); this.loadingSubject.next(false);
}); });

View File

@@ -9,14 +9,14 @@
class="logo" class="logo"
alt="home logo" alt="home logo"
*ngIf="isDarkTheme; else customlighttheme" *ngIf="isDarkTheme; else customlighttheme"
[src]="labelpolicy?.iconUrlDark ? labelpolicy.iconUrlDark : './assets/images/zitadel-logo-solo-light.svg'" [src]="labelpolicy.iconUrlDark ? labelpolicy.iconUrlDark : './assets/images/zitadel-logo-solo-light.svg'"
(error)="errorHandler($event, './assets/images/zitadel-logo-solo-light.svg')" (error)="errorHandler($event, './assets/images/zitadel-logo-solo-light.svg')"
/> />
<ng-template #customlighttheme> <ng-template #customlighttheme>
<img <img
alt="home logo" alt="home logo"
class="logo" class="logo"
[src]="labelpolicy?.iconUrl ? labelpolicy.iconUrl : './assets/images/zitadel-logo-solo-dark.svg'" [src]="labelpolicy.iconUrl ? labelpolicy.iconUrl : './assets/images/zitadel-logo-solo-dark.svg'"
(error)="errorHandler($event, './assets/images/zitadel-logo-solo-dark.svg')" (error)="errorHandler($event, './assets/images/zitadel-logo-solo-dark.svg')"
/> />
</ng-template> </ng-template>
@@ -78,7 +78,7 @@
<div class="org-context"> <div class="org-context">
<a *ngIf="org" matRipple [matRippleUnbounded]="false" class="org-link" id="orglink" [routerLink]="['/org']"> <a *ngIf="org" matRipple [matRippleUnbounded]="false" class="org-link" id="orglink" [routerLink]="['/org']">
{{ org?.name ? org.name : 'NO NAME' }}</a {{ org.name ? org.name : 'NO NAME' }}</a
> >
<div class="org-context-wrapper" *ngIf="org"> <div class="org-context-wrapper" *ngIf="org">
@@ -212,7 +212,7 @@
class="avatar-toggle dontcloseonclick" class="avatar-toggle dontcloseonclick"
[active]="showAccount" [active]="showAccount"
[avatarUrl]="user.human?.profile?.avatarUrl || ''" [avatarUrl]="user.human?.profile?.avatarUrl || ''"
[forColor]="user?.preferredLoginName || ''" [forColor]="user.preferredLoginName || ''"
[name]=" [name]="
user.human?.profile?.displayName user.human?.profile?.displayName
? user.human?.profile?.displayName ?? '' ? user.human?.profile?.displayName ?? ''

View File

@@ -21,8 +21,8 @@ export class HeaderComponent implements OnDestroy {
@ViewChild('input', { static: false }) input!: ElementRef; @ViewChild('input', { static: false }) input!: ElementRef;
@Input() public isDarkTheme: boolean = true; @Input() public isDarkTheme: boolean = true;
@Input() public user!: User.AsObject; @Input() public user?: User.AsObject;
@Input() public labelpolicy!: LabelPolicy.AsObject; @Input() public labelpolicy?: LabelPolicy.AsObject;
public showOrgContext: boolean = false; public showOrgContext: boolean = false;
public orgs$: Observable<Org.AsObject[]> = of([]); public orgs$: Observable<Org.AsObject[]> = of([]);

View File

@@ -33,7 +33,7 @@ export class IdpTableComponent implements OnInit {
@ViewChild(PaginatorComponent) public paginator!: PaginatorComponent; @ViewChild(PaginatorComponent) public paginator!: PaginatorComponent;
public dataSource: MatTableDataSource<IDP.AsObject> = new MatTableDataSource<IDP.AsObject>(); public dataSource: MatTableDataSource<IDP.AsObject> = new MatTableDataSource<IDP.AsObject>();
public selection: SelectionModel<IDP.AsObject> = new SelectionModel<IDP.AsObject>(true, []); public selection: SelectionModel<IDP.AsObject> = new SelectionModel<IDP.AsObject>(true, []);
public idpResult!: ListIDPsResponse.AsObject | ListOrgIDPsResponse.AsObject; public idpResult?: ListIDPsResponse.AsObject | ListOrgIDPsResponse.AsObject;
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false); private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
public loading$: Observable<boolean> = this.loadingSubject.asObservable(); public loading$: Observable<boolean> = this.loadingSubject.asObservable();
public PolicyComponentServiceType: any = PolicyComponentServiceType; public PolicyComponentServiceType: any = PolicyComponentServiceType;

View File

@@ -1,5 +1,5 @@
<cnsl-top-view <cnsl-top-view
title="{{ idp?.name ? idp.name : ('IDP.DETAIL.TITLE' | translate) }}" title="{{ idp?.name ? idp?.name : ('IDP.DETAIL.TITLE' | translate) }}"
[sub]="idp?.oidcConfig ? ('IDP.TYPES.1' | translate) : idp?.jwtConfig ? ('IDP.TYPES.3' | translate) : ''" [sub]="idp?.oidcConfig ? ('IDP.TYPES.1' | translate) : idp?.jwtConfig ? ('IDP.TYPES.3' | translate) : ''"
[isActive]="idp?.state === IDPState.IDP_STATE_ACTIVE" [isActive]="idp?.state === IDPState.IDP_STATE_ACTIVE"
[isInactive]="idp?.state === IDPState.IDP_STATE_INACTIVE" [isInactive]="idp?.state === IDPState.IDP_STATE_INACTIVE"

View File

@@ -42,7 +42,7 @@ export class IdpComponent implements OnDestroy {
public PolicyComponentServiceType: any = PolicyComponentServiceType; public PolicyComponentServiceType: any = PolicyComponentServiceType;
public readonly separatorKeysCodes: number[] = [ENTER, COMMA, SPACE]; public readonly separatorKeysCodes: number[] = [ENTER, COMMA, SPACE];
public idp!: IDP.AsObject; public idp?: IDP.AsObject;
private destroy$: Subject<void> = new Subject(); private destroy$: Subject<void> = new Subject();
public projectId: string = ''; public projectId: string = '';
@@ -189,7 +189,7 @@ export class IdpComponent implements OnDestroy {
}); });
dialogRef.afterClosed().subscribe((resp) => { dialogRef.afterClosed().subscribe((resp) => {
if (resp) { if (this.idp && resp) {
if (this.serviceType === PolicyComponentServiceType.MGMT) { if (this.serviceType === PolicyComponentServiceType.MGMT) {
(this.service as ManagementService) (this.service as ManagementService)
.removeOrgIDP(this.idp.id) .removeOrgIDP(this.idp.id)
@@ -217,21 +217,21 @@ export class IdpComponent implements OnDestroy {
public changeState(state: IDPState): void { public changeState(state: IDPState): void {
if (this.serviceType === PolicyComponentServiceType.MGMT) { if (this.serviceType === PolicyComponentServiceType.MGMT) {
if (state === IDPState.IDP_STATE_ACTIVE) { if (state === IDPState.IDP_STATE_ACTIVE && this.idp) {
(this.service as ManagementService) (this.service as ManagementService)
.reactivateOrgIDP(this.idp.id) .reactivateOrgIDP(this.idp.id)
.then(() => { .then(() => {
this.idp.state = state; this.idp!.state = state;
this.toast.showInfo('IDP.TOAST.REACTIVATED', true); this.toast.showInfo('IDP.TOAST.REACTIVATED', true);
}) })
.catch((error: any) => { .catch((error: any) => {
this.toast.showError(error); this.toast.showError(error);
}); });
} else if (state === IDPState.IDP_STATE_INACTIVE) { } else if (state === IDPState.IDP_STATE_INACTIVE && this.idp) {
(this.service as ManagementService) (this.service as ManagementService)
.deactivateOrgIDP(this.idp.id) .deactivateOrgIDP(this.idp.id)
.then(() => { .then(() => {
this.idp.state = state; this.idp!.state = state;
this.toast.showInfo('IDP.TOAST.DEACTIVATED', true); this.toast.showInfo('IDP.TOAST.DEACTIVATED', true);
}) })
.catch((error: any) => { .catch((error: any) => {
@@ -239,21 +239,21 @@ export class IdpComponent implements OnDestroy {
}); });
} }
} else if (this.serviceType === PolicyComponentServiceType.ADMIN) { } else if (this.serviceType === PolicyComponentServiceType.ADMIN) {
if (state === IDPState.IDP_STATE_ACTIVE) { if (state === IDPState.IDP_STATE_ACTIVE && this.idp) {
(this.service as AdminService) (this.service as AdminService)
.reactivateIDP(this.idp.id) .reactivateIDP(this.idp.id)
.then(() => { .then(() => {
this.idp.state = state; this.idp!.state = state;
this.toast.showInfo('IDP.TOAST.REACTIVATED', true); this.toast.showInfo('IDP.TOAST.REACTIVATED', true);
}) })
.catch((error: any) => { .catch((error: any) => {
this.toast.showError(error); this.toast.showError(error);
}); });
} else if (state === IDPState.IDP_STATE_INACTIVE) { } else if (state === IDPState.IDP_STATE_INACTIVE && this.idp) {
(this.service as AdminService) (this.service as AdminService)
.deactivateIDP(this.idp.id) .deactivateIDP(this.idp.id)
.then(() => { .then(() => {
this.idp.state = state; this.idp!.state = state;
this.toast.showInfo('IDP.TOAST.DEACTIVATED', true); this.toast.showInfo('IDP.TOAST.DEACTIVATED', true);
}) })
.catch((error: any) => { .catch((error: any) => {
@@ -264,7 +264,7 @@ export class IdpComponent implements OnDestroy {
} }
public updateIdp(): void { public updateIdp(): void {
if (this.serviceType === PolicyComponentServiceType.MGMT) { if (this.serviceType === PolicyComponentServiceType.MGMT && this.idp) {
const req = new UpdateOrgIDPRequest(); const req = new UpdateOrgIDPRequest();
req.setIdpId(this.idp.id); req.setIdpId(this.idp.id);
@@ -280,7 +280,7 @@ export class IdpComponent implements OnDestroy {
.catch((error) => { .catch((error) => {
this.toast.showError(error); this.toast.showError(error);
}); });
} else if (this.serviceType === PolicyComponentServiceType.ADMIN) { } else if (this.serviceType === PolicyComponentServiceType.ADMIN && this.idp) {
const req = new UpdateIDPRequest(); const req = new UpdateIDPRequest();
req.setIdpId(this.idp.id); req.setIdpId(this.idp.id);
@@ -300,7 +300,7 @@ export class IdpComponent implements OnDestroy {
} }
public updateOidcConfig(): void { public updateOidcConfig(): void {
if (this.serviceType === PolicyComponentServiceType.MGMT) { if (this.serviceType === PolicyComponentServiceType.MGMT && this.idp) {
const req = new UpdateOrgIDPOIDCConfigRequest(); const req = new UpdateOrgIDPOIDCConfigRequest();
req.setIdpId(this.idp.id); req.setIdpId(this.idp.id);
@@ -319,7 +319,7 @@ export class IdpComponent implements OnDestroy {
.catch((error) => { .catch((error) => {
this.toast.showError(error); this.toast.showError(error);
}); });
} else if (this.serviceType === PolicyComponentServiceType.ADMIN) { } else if (this.serviceType === PolicyComponentServiceType.ADMIN && this.idp) {
const req = new UpdateIDPOIDCConfigRequest(); const req = new UpdateIDPOIDCConfigRequest();
req.setIdpId(this.idp.id); req.setIdpId(this.idp.id);
@@ -342,7 +342,7 @@ export class IdpComponent implements OnDestroy {
} }
public updateJwtConfig(): void { public updateJwtConfig(): void {
if (this.serviceType === PolicyComponentServiceType.MGMT) { if (this.serviceType === PolicyComponentServiceType.MGMT && this.idp) {
const req = new UpdateOrgIDPJWTConfigRequest(); const req = new UpdateOrgIDPJWTConfigRequest();
req.setIdpId(this.idp.id); req.setIdpId(this.idp.id);
@@ -360,7 +360,7 @@ export class IdpComponent implements OnDestroy {
.catch((error) => { .catch((error) => {
this.toast.showError(error); this.toast.showError(error);
}); });
} else if (this.serviceType === PolicyComponentServiceType.ADMIN) { } else if (this.serviceType === PolicyComponentServiceType.ADMIN && this.idp) {
const req = new UpdateIDPJWTConfigRequest(); const req = new UpdateIDPJWTConfigRequest();
req.setIdpId(this.idp.id); req.setIdpId(this.idp.id);

View File

@@ -257,31 +257,31 @@
</p> </p>
</div> </div>
<div class="info-wrapper"> <div class="info-wrapper" *ngIf="app">
<p class="info-row-title">{{ 'APP.OIDC.INFO.CLIENTID' | translate }}</p> <p class="info-row-title">{{ 'APP.OIDC.INFO.CLIENTID' | translate }}</p>
<div class="copy-row" *ngIf="app?.oidcConfig?.clientId"> <div class="copy-row" *ngIf="app.oidcConfig?.clientId">
<button <button
*ngIf="app.oidcConfig && app.oidcConfig?.clientId" *ngIf="app.oidcConfig && app.oidcConfig?.clientId"
[disabled]="copied === app.oidcConfig?.clientId" [disabled]="copied === app.oidcConfig.clientId"
[matTooltip]="(copied !== app.oidcConfig?.clientId ? 'ACTIONS.COPY' : 'ACTIONS.COPIED') | translate" [matTooltip]="(copied !== app.oidcConfig.clientId ? 'ACTIONS.COPY' : 'ACTIONS.COPIED') | translate"
cnslCopyToClipboard cnslCopyToClipboard
[valueToCopy]="app.oidcConfig.clientId" [valueToCopy]="app.oidcConfig.clientId"
(copiedValue)="copied = $event" (copiedValue)="copied = $event"
> >
{{ app.oidcConfig?.clientId }} {{ app.oidcConfig.clientId }}
</button> </button>
</div> </div>
<div class="copy-row" *ngIf="app?.apiConfig?.clientId"> <div class="copy-row" *ngIf="app.apiConfig?.clientId">
<button <button
*ngIf="app && app.apiConfig && app.apiConfig.clientId" *ngIf="app && app.apiConfig && app.apiConfig.clientId"
[disabled]="copied === app.apiConfig?.clientId" [disabled]="copied === app.apiConfig.clientId"
[matTooltip]="(copied !== app.apiConfig?.clientId ? 'ACTIONS.COPY' : 'ACTIONS.COPIED') | translate" [matTooltip]="(copied !== app.apiConfig.clientId ? 'ACTIONS.COPY' : 'ACTIONS.COPIED') | translate"
cnslCopyToClipboard cnslCopyToClipboard
[valueToCopy]="app.apiConfig.clientId" [valueToCopy]="app.apiConfig.clientId"
(copiedValue)="copied = $event" (copiedValue)="copied = $event"
> >
{{ app.apiConfig?.clientId }} {{ app.apiConfig.clientId }}
</button> </button>
</div> </div>
</div> </div>

View File

@@ -22,12 +22,12 @@ import { WarnDialogComponent } from '../warn-dialog/warn-dialog.component';
styleUrls: ['./machine-keys.component.scss'], styleUrls: ['./machine-keys.component.scss'],
}) })
export class MachineKeysComponent implements OnInit { export class MachineKeysComponent implements OnInit {
@Input() userId!: string; @Input() userId?: string;
@ViewChild(PaginatorComponent) public paginator!: PaginatorComponent; @ViewChild(PaginatorComponent) public paginator!: PaginatorComponent;
public dataSource: MatTableDataSource<Key.AsObject> = new MatTableDataSource<Key.AsObject>(); public dataSource: MatTableDataSource<Key.AsObject> = new MatTableDataSource<Key.AsObject>();
public selection: SelectionModel<Key.AsObject> = new SelectionModel<Key.AsObject>(true, []); public selection: SelectionModel<Key.AsObject> = new SelectionModel<Key.AsObject>(true, []);
public keyResult!: ListMachineKeysResponse.AsObject; public keyResult?: ListMachineKeysResponse.AsObject;
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false); private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
public loading$: Observable<boolean> = this.loadingSubject.asObservable(); public loading$: Observable<boolean> = this.loadingSubject.asObservable();
@Input() public displayedColumns: string[] = ['id', 'type', 'creationDate', 'expirationDate', 'actions']; @Input() public displayedColumns: string[] = ['id', 'type', 'creationDate', 'expirationDate', 'actions'];
@@ -64,6 +64,7 @@ export class MachineKeysComponent implements OnInit {
} }
public deleteKey(key: Key.AsObject): void { public deleteKey(key: Key.AsObject): void {
if (this.userId) {
const dialogRef = this.dialog.open(WarnDialogComponent, { const dialogRef = this.dialog.open(WarnDialogComponent, {
data: { data: {
confirmKey: 'ACTIONS.DELETE', confirmKey: 'ACTIONS.DELETE',
@@ -73,8 +74,9 @@ export class MachineKeysComponent implements OnInit {
}, },
width: '400px', width: '400px',
}); });
dialogRef.afterClosed().subscribe((resp) => { dialogRef.afterClosed().subscribe((resp) => {
if (resp) { if (resp && this.userId) {
this.mgmtService this.mgmtService
.removeMachineKey(key.id, this.userId) .removeMachineKey(key.id, this.userId)
.then(() => { .then(() => {
@@ -88,6 +90,7 @@ export class MachineKeysComponent implements OnInit {
} }
}); });
} }
}
public openAddKey(): void { public openAddKey(): void {
const dialogRef = this.dialog.open(AddKeyDialogComponent, { const dialogRef = this.dialog.open(AddKeyDialogComponent, {
@@ -111,7 +114,7 @@ export class MachineKeysComponent implements OnInit {
date = ts; date = ts;
} }
if (type) { if (type && this.userId) {
this.mgmtService this.mgmtService
.addMachineKey(this.userId, type, date) .addMachineKey(this.userId, type, date)
.then((response) => { .then((response) => {

View File

@@ -1,7 +1,12 @@
<cnsl-refresh-table *ngIf="dataSource" (refreshed)="changePage()" [dataSize]="dataSource.totalResult" <cnsl-refresh-table
[timestamp]="dataSource.viewTimestamp" [hideRefresh]="true" [selection]="selection" *ngIf="dataSource"
[loading]="dataSource?.loading$ | async"> (refreshed)="changePage()"
[dataSize]="dataSource.totalResult"
[timestamp]="dataSource.viewTimestamp"
[hideRefresh]="true"
[selection]="selection"
[loading]="dataSource.loading$ | async"
>
<ng-container actions *ngIf="selection.hasValue()"> <ng-container actions *ngIf="selection.hasValue()">
<ng-content select="[selectactions]"></ng-content> <ng-content select="[selectactions]"></ng-content>
</ng-container> </ng-container>
@@ -14,15 +19,23 @@
<table mat-table class="table" aria-label="Elements" [dataSource]="dataSource"> <table mat-table class="table" aria-label="Elements" [dataSource]="dataSource">
<ng-container matColumnDef="select"> <ng-container matColumnDef="select">
<th class="selection" mat-header-cell *matHeaderCellDef> <th class="selection" mat-header-cell *matHeaderCellDef>
<mat-checkbox [disabled]="false" color="primary" (change)="$event ? masterToggle() : null" <mat-checkbox
[disabled]="false"
color="primary"
(change)="$event ? masterToggle() : null"
[checked]="selection.hasValue() && isAllSelected()" [checked]="selection.hasValue() && isAllSelected()"
[indeterminate]="selection.hasValue() && !isAllSelected()"> [indeterminate]="selection.hasValue() && !isAllSelected()"
>
</mat-checkbox> </mat-checkbox>
</th> </th>
<td class="selection" mat-cell *matCellDef="let row"> <td class="selection" mat-cell *matCellDef="let row">
<mat-checkbox [disabled]="false" color="primary" (click)="$event.stopPropagation()" <mat-checkbox
(change)="$event ? selection.toggle(row) : null" [checked]="selection.isSelected(row)"> [disabled]="false"
color="primary"
(click)="$event.stopPropagation()"
(change)="$event ? selection.toggle(row) : null"
[checked]="selection.isSelected(row)"
>
</mat-checkbox> </mat-checkbox>
</td> </td>
</ng-container> </ng-container>
@@ -30,7 +43,8 @@
<ng-container matColumnDef="displayName"> <ng-container matColumnDef="displayName">
<th mat-header-cell *matHeaderCellDef>{{ 'USER.MEMBERSHIPS.DISPLAYNAME' | translate }}</th> <th mat-header-cell *matHeaderCellDef>{{ 'USER.MEMBERSHIPS.DISPLAYNAME' | translate }}</th>
<td mat-cell *matCellDef="let membership"> <td mat-cell *matCellDef="let membership">
{{membership.displayName}} </td> {{ membership.displayName }}
</td>
</ng-container> </ng-container>
<ng-container matColumnDef="type"> <ng-container matColumnDef="type">
@@ -43,7 +57,8 @@
<ng-container matColumnDef="orgId"> <ng-container matColumnDef="orgId">
<th mat-header-cell *matHeaderCellDef>{{ 'USER.MEMBERSHIPS.ORGID' | translate }}</th> <th mat-header-cell *matHeaderCellDef>{{ 'USER.MEMBERSHIPS.ORGID' | translate }}</th>
<td mat-cell *matCellDef="let membership"> <td mat-cell *matCellDef="let membership">
{{membership.orgId}} </td> {{ membership.orgId }}
</td>
</ng-container> </ng-container>
<ng-container matColumnDef="rolesList"> <ng-container matColumnDef="rolesList">
@@ -51,8 +66,13 @@
<td mat-cell *matCellDef="let membership"> <td mat-cell *matCellDef="let membership">
<div class="membership-line"> <div class="membership-line">
<mat-chip-list class="cnsl-chip-list" aria-label="role selection"> <mat-chip-list class="cnsl-chip-list" aria-label="role selection">
<mat-chip class="cnsl-chip" *ngFor="let role of membership.rolesList" [removable]="!!userId" <mat-chip
[selectable]="false" (removed)="removeRole(membership, role)"> class="cnsl-chip"
*ngFor="let role of membership.rolesList"
[removable]="!!userId"
[selectable]="false"
(removed)="removeRole(membership, role)"
>
<div class="cnsl-chip-dot" [style.background]="getColor(role)"></div> <div class="cnsl-chip-dot" [style.background]="getColor(role)"></div>
<span>{{ role | roletransform }}</span> <span>{{ role | roletransform }}</span>
<button *ngIf="!!userId" matChipRemove> <button *ngIf="!!userId" matChipRemove>
@@ -60,8 +80,12 @@
</button> </button>
</mat-chip> </mat-chip>
</mat-chip-list> </mat-chip-list>
<button matTooltip="{{'ACTIONS.EDIT' | translate}}" (click)="goto(membership)" mat-icon-button <button
class="goto-btn"> matTooltip="{{ 'ACTIONS.EDIT' | translate }}"
(click)="goto(membership)"
mat-icon-button
class="goto-btn"
>
<i class="las la-angle-right"></i> <i class="las la-angle-right"></i>
</button> </button>
</div> </div>
@@ -69,12 +93,18 @@
</ng-container> </ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr class="highlight" mat-row *matRowDef="let row; columns: displayedColumns;"> <tr class="highlight" mat-row *matRowDef="let row; columns: displayedColumns"></tr>
</tr>
</table> </table>
</div> </div>
<cnsl-paginator *ngIf="dataSource" class="paginator" #paginator [timestamp]="dataSource?.viewTimestamp" <cnsl-paginator
[pageSize]="INITIALPAGESIZE" [length]="dataSource.totalResult" [pageSizeOptions]="[25, 50, 100, 250]" *ngIf="dataSource"
(page)="changePage($event)"> class="paginator"
#paginator
[timestamp]="dataSource.viewTimestamp"
[pageSize]="INITIALPAGESIZE"
[length]="dataSource.totalResult"
[pageSizeOptions]="[25, 50, 100, 250]"
(page)="changePage($event)"
>
</cnsl-paginator> </cnsl-paginator>
</cnsl-refresh-table> </cnsl-refresh-table>

View File

@@ -28,8 +28,8 @@ export class MembershipsTableComponent implements OnInit, OnDestroy {
@ViewChild(PaginatorComponent) public paginator!: PaginatorComponent; @ViewChild(PaginatorComponent) public paginator!: PaginatorComponent;
@ViewChild(MatTable) public table!: MatTable<Membership.AsObject>; @ViewChild(MatTable) public table!: MatTable<Membership.AsObject>;
@Input() public userId: string = ''; @Input() public userId: string = '';
public dataSource!: MembershipsDataSource; public dataSource: MembershipsDataSource = new MembershipsDataSource(this.authService, this.mgmtService);
public selection: SelectionModel<any> = new SelectionModel<any>(true, []); public selection: SelectionModel<Membership.AsObject> = new SelectionModel<Membership.AsObject>(true, []);
@Output() public changedSelection: EventEmitter<any[]> = new EventEmitter(); @Output() public changedSelection: EventEmitter<any[]> = new EventEmitter();
@Output() public deleteMembership: EventEmitter<Membership.AsObject> = new EventEmitter(); @Output() public deleteMembership: EventEmitter<Membership.AsObject> = new EventEmitter();
@@ -51,8 +51,6 @@ export class MembershipsTableComponent implements OnInit, OnDestroy {
private workflowService: OverlayWorkflowService, private workflowService: OverlayWorkflowService,
private storageService: StorageService, private storageService: StorageService,
) { ) {
this.dataSource = new MembershipsDataSource(this.authService, this.mgmtService);
this.selection.changed.pipe(takeUntil(this.destroyed)).subscribe((_) => { this.selection.changed.pipe(takeUntil(this.destroyed)).subscribe((_) => {
this.changedSelection.emit(this.selection.selected); this.changedSelection.emit(this.selection.selected);
}); });

View File

@@ -1,12 +1,19 @@
<div class="validation-col" *ngIf="this.policy"> <div class="validation-col" *ngIf="policy">
<div class="val" *ngIf="this.policy.minLength"> <div class="val" *ngIf="policy.minLength">
<i *ngIf="password?.value?.length === 0; else showSpinner" class="las la-times red"></i> <i *ngIf="password?.value?.length === 0; else showSpinner" class="las la-times red"></i>
<ng-template #showSpinner> <ng-template #showSpinner>
<div *ngIf="(password?.errors?.minlength || password?.value?.length === 0) as currentError; else trueminlength" <div
class="complexity-sp-wrapper"> *ngIf="password?.errors?.minlength || password?.value?.length === 0 as currentError; else trueminlength"
<mat-progress-spinner class="complexity-spinner" diameter="20" [color]="currentError ? 'warn': 'valid'" class="complexity-sp-wrapper"
mode="determinate" [value]="(password?.value?.length / policy.minLength) * 100"> >
<mat-progress-spinner
class="complexity-spinner"
diameter="20"
[color]="currentError ? 'warn' : 'valid'"
mode="determinate"
[value]="(password?.value?.length / policy.minLength) * 100"
>
</mat-progress-spinner> </mat-progress-spinner>
</div> </div>
</ng-template> </ng-template>
@@ -14,8 +21,10 @@
<i class="las la-check green"></i> <i class="las la-check green"></i>
</ng-template> </ng-template>
<span class="cnsl-secondary-text">{{ 'USER.PASSWORD.MINLENGTHERROR' | translate: {value: policy?.minLength} }} <span class="cnsl-secondary-text"
({{password?.value?.length}}/{{ policy.minLength}}) >{{ 'USER.PASSWORD.MINLENGTHERROR' | translate: { value: policy.minLength } }} ({{ password?.value?.length }}/{{
policy.minLength
}})
</span> </span>
</div> </div>
<div class="val" *ngIf="this.policy.hasSymbol"> <div class="val" *ngIf="this.policy.hasSymbol">

View File

@@ -57,7 +57,7 @@
(typeRemoved)="removeFactor($event)" (typeRemoved)="removeFactor($event)"
(typeAdded)="addFactor($event)" (typeAdded)="addFactor($event)"
[disabled]=" [disabled]="
loginData?.passwordlessType === PasswordlessType.PASSWORDLESS_TYPE_NOT_ALLOWED || loginData.passwordlessType === PasswordlessType.PASSWORDLESS_TYPE_NOT_ALLOWED ||
([ ([
serviceType === PolicyComponentServiceType.ADMIN serviceType === PolicyComponentServiceType.ADMIN
? 'iam.policy.write' ? 'iam.policy.write'

View File

@@ -34,7 +34,7 @@ export class LoginPolicyComponent implements OnInit {
PasswordlessType.PASSWORDLESS_TYPE_NOT_ALLOWED, PasswordlessType.PASSWORDLESS_TYPE_NOT_ALLOWED,
PasswordlessType.PASSWORDLESS_TYPE_ALLOWED, PasswordlessType.PASSWORDLESS_TYPE_ALLOWED,
]; ];
public loginData!: LoginPolicy.AsObject; public loginData?: LoginPolicy.AsObject;
public service!: ManagementService | AdminService; public service!: ManagementService | AdminService;
public PolicyComponentServiceType: any = PolicyComponentServiceType; public PolicyComponentServiceType: any = PolicyComponentServiceType;
@@ -43,22 +43,20 @@ export class LoginPolicyComponent implements OnInit {
public loading: boolean = false; public loading: boolean = false;
public InfoSectionType: any = InfoSectionType; public InfoSectionType: any = InfoSectionType;
public PasswordlessType: any = PasswordlessType; public PasswordlessType: any = PasswordlessType;
public lifetimeForm!: UntypedFormGroup; public lifetimeForm: UntypedFormGroup = this.fb.group({
constructor(
private toast: ToastService,
private injector: Injector,
private fb: UntypedFormBuilder,
private authService: GrpcAuthService,
private dialog: MatDialog,
) {
this.lifetimeForm = this.fb.group({
passwordCheckLifetime: [{ disabled: true, value: 240 }, [Validators.required]], passwordCheckLifetime: [{ disabled: true, value: 240 }, [Validators.required]],
externalLoginCheckLifetime: [{ disabled: true, value: 12 }, [Validators.required]], externalLoginCheckLifetime: [{ disabled: true, value: 12 }, [Validators.required]],
mfaInitSkipLifetime: [{ disabled: true, value: 720 }, [Validators.required]], mfaInitSkipLifetime: [{ disabled: true, value: 720 }, [Validators.required]],
secondFactorCheckLifetime: [{ disabled: true, value: 12 }, [Validators.required]], secondFactorCheckLifetime: [{ disabled: true, value: 12 }, [Validators.required]],
multiFactorCheckLifetime: [{ disabled: true, value: 12 }, [Validators.required]], multiFactorCheckLifetime: [{ disabled: true, value: 12 }, [Validators.required]],
}); });
} constructor(
private toast: ToastService,
private injector: Injector,
private fb: UntypedFormBuilder,
private authService: GrpcAuthService,
private dialog: MatDialog,
) {}
public fetchData(): void { public fetchData(): void {
this.getData() this.getData()
@@ -143,6 +141,7 @@ export class LoginPolicyComponent implements OnInit {
} }
private async updateData(): Promise<UpdateLoginPolicyResponse.AsObject> { private async updateData(): Promise<UpdateLoginPolicyResponse.AsObject> {
if (this.loginData) {
switch (this.serviceType) { switch (this.serviceType) {
case PolicyComponentServiceType.MGMT: case PolicyComponentServiceType.MGMT:
const mgmtreq = new AddCustomLoginPolicyRequest(); const mgmtreq = new AddCustomLoginPolicyRequest();
@@ -206,6 +205,9 @@ export class LoginPolicyComponent implements OnInit {
return (this.service as AdminService).updateLoginPolicy(adminreq); return (this.service as AdminService).updateLoginPolicy(adminreq);
} }
} else {
return Promise.reject();
}
} }
public savePolicy(): void { public savePolicy(): void {

View File

@@ -72,10 +72,10 @@
*ngIf="twilio" *ngIf="twilio"
class="state" class="state"
[ngClass]="{ [ngClass]="{
active: twilio?.state === SMSProviderConfigState.SMS_PROVIDER_CONFIG_ACTIVE, active: twilio.state === SMSProviderConfigState.SMS_PROVIDER_CONFIG_ACTIVE,
inactive: twilio?.state === SMSProviderConfigState.SMS_PROVIDER_CONFIG_INACTIVE inactive: twilio.state === SMSProviderConfigState.SMS_PROVIDER_CONFIG_INACTIVE
}" }"
>{{ 'SETTING.SMS.SMSPROVIDERSTATE.' + twilio?.state | translate }}</span >{{ 'SETTING.SMS.SMSPROVIDERSTATE.' + twilio.state | translate }}</span
> >
<span class="fill-space"></span> <span class="fill-space"></span>

View File

@@ -28,7 +28,7 @@
<button mat-icon-button (click)="decrementLength()" [disabled]="(['policy.write'] | hasRole | async) === false"> <button mat-icon-button (click)="decrementLength()" [disabled]="(['policy.write'] | hasRole | async) === false">
<mat-icon>remove</mat-icon> <mat-icon>remove</mat-icon>
</button> </button>
<span>{{ complexityData?.minLength }}</span> <span>{{ complexityData.minLength }}</span>
<button mat-icon-button (click)="incrementLength()" [disabled]="(['policy.write'] | hasRole | async) === false"> <button mat-icon-button (click)="incrementLength()" [disabled]="(['policy.write'] | hasRole | async) === false">
<mat-icon>add</mat-icon> <mat-icon>add</mat-icon>
</button> </button>

View File

@@ -1,11 +1,7 @@
import { Component, Injector, Input, OnInit, Type } from '@angular/core'; import { Component, Injector, Input, OnInit, Type } from '@angular/core';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { import { GetPasswordComplexityPolicyResponse as AdminGetPasswordComplexityPolicyResponse } from 'src/app/proto/generated/zitadel/admin_pb';
GetPasswordComplexityPolicyResponse as AdminGetPasswordComplexityPolicyResponse, import { GetPasswordComplexityPolicyResponse as MgmtGetPasswordComplexityPolicyResponse } from 'src/app/proto/generated/zitadel/management_pb';
} from 'src/app/proto/generated/zitadel/admin_pb';
import {
GetPasswordComplexityPolicyResponse as MgmtGetPasswordComplexityPolicyResponse,
} from 'src/app/proto/generated/zitadel/management_pb';
import { PasswordComplexityPolicy } from 'src/app/proto/generated/zitadel/policy_pb'; import { PasswordComplexityPolicy } from 'src/app/proto/generated/zitadel/policy_pb';
import { AdminService } from 'src/app/services/admin.service'; import { AdminService } from 'src/app/services/admin.service';
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
@@ -24,7 +20,7 @@ export class PasswordComplexityPolicyComponent implements OnInit {
@Input() public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT; @Input() public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT;
public service!: ManagementService | AdminService; public service!: ManagementService | AdminService;
public complexityData!: PasswordComplexityPolicy.AsObject; public complexityData?: PasswordComplexityPolicy.AsObject;
public PolicyComponentServiceType: any = PolicyComponentServiceType; public PolicyComponentServiceType: any = PolicyComponentServiceType;
@@ -110,6 +106,7 @@ export class PasswordComplexityPolicyComponent implements OnInit {
} }
public savePolicy(): void { public savePolicy(): void {
if (this.complexityData) {
switch (this.serviceType) { switch (this.serviceType) {
case PolicyComponentServiceType.MGMT: case PolicyComponentServiceType.MGMT:
if ((this.complexityData as PasswordComplexityPolicy.AsObject).isDefault) { if ((this.complexityData as PasswordComplexityPolicy.AsObject).isDefault) {
@@ -162,6 +159,7 @@ export class PasswordComplexityPolicyComponent implements OnInit {
break; break;
} }
} }
}
public get isDefault(): boolean { public get isDefault(): boolean {
if (this.complexityData && this.serviceType === PolicyComponentServiceType.MGMT) { if (this.complexityData && this.serviceType === PolicyComponentServiceType.MGMT) {

View File

@@ -13,8 +13,6 @@
</button> </button>
</ng-template> </ng-template>
<!-- <cnsl-info-section class="default" *ngIf="isDefault"> {{ 'POLICY.DEFAULTLABEL' | translate }}</cnsl-info-section> -->
<cnsl-card *ngIf="lockoutData"> <cnsl-card *ngIf="lockoutData">
<div class="lockout-content"> <div class="lockout-content">
<div class="row"> <div class="row">
@@ -22,7 +20,7 @@
<button [disabled]="(['policy.write'] | hasRole | async) === false" mat-icon-button (click)="decrementMaxAttempts()"> <button [disabled]="(['policy.write'] | hasRole | async) === false" mat-icon-button (click)="decrementMaxAttempts()">
<mat-icon>remove</mat-icon> <mat-icon>remove</mat-icon>
</button> </button>
<span>{{ lockoutData?.maxPasswordAttempts }}</span> <span>{{ lockoutData.maxPasswordAttempts }}</span>
<button [disabled]="(['policy.write'] | hasRole | async) === false" mat-icon-button (click)="incrementMaxAttempts()"> <button [disabled]="(['policy.write'] | hasRole | async) === false" mat-icon-button (click)="incrementMaxAttempts()">
<mat-icon>add</mat-icon> <mat-icon>add</mat-icon>
</button> </button>

View File

@@ -2,9 +2,7 @@ import { Component, Injector, Input, OnInit, Type } from '@angular/core';
import { UntypedFormGroup } from '@angular/forms'; import { UntypedFormGroup } from '@angular/forms';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { GetLockoutPolicyResponse as AdminGetPasswordLockoutPolicyResponse } from 'src/app/proto/generated/zitadel/admin_pb'; import { GetLockoutPolicyResponse as AdminGetPasswordLockoutPolicyResponse } from 'src/app/proto/generated/zitadel/admin_pb';
import { import { GetLockoutPolicyResponse as MgmtGetPasswordLockoutPolicyResponse } from 'src/app/proto/generated/zitadel/management_pb';
GetLockoutPolicyResponse as MgmtGetPasswordLockoutPolicyResponse,
} from 'src/app/proto/generated/zitadel/management_pb';
import { LockoutPolicy } from 'src/app/proto/generated/zitadel/policy_pb'; import { LockoutPolicy } from 'src/app/proto/generated/zitadel/policy_pb';
import { AdminService } from 'src/app/services/admin.service'; import { AdminService } from 'src/app/services/admin.service';
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
@@ -24,7 +22,7 @@ export class PasswordLockoutPolicyComponent implements OnInit {
@Input() public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT; @Input() public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT;
public lockoutForm!: UntypedFormGroup; public lockoutForm!: UntypedFormGroup;
public lockoutData!: LockoutPolicy.AsObject; public lockoutData?: LockoutPolicy.AsObject;
public PolicyComponentServiceType: any = PolicyComponentServiceType; public PolicyComponentServiceType: any = PolicyComponentServiceType;
public InfoSectionType: any = InfoSectionType; public InfoSectionType: any = InfoSectionType;
@@ -103,6 +101,7 @@ export class PasswordLockoutPolicyComponent implements OnInit {
public savePolicy(): void { public savePolicy(): void {
let promise: Promise<any>; let promise: Promise<any>;
if (this.lockoutData) {
if (this.service instanceof AdminService) { if (this.service instanceof AdminService) {
promise = this.service promise = this.service
.updateLockoutPolicy(this.lockoutData.maxPasswordAttempts) .updateLockoutPolicy(this.lockoutData.maxPasswordAttempts)
@@ -115,7 +114,7 @@ export class PasswordLockoutPolicyComponent implements OnInit {
}); });
} else { } else {
if ((this.lockoutData as LockoutPolicy.AsObject).isDefault) { if ((this.lockoutData as LockoutPolicy.AsObject).isDefault) {
promise = this.service promise = (this.service as ManagementService)
.addCustomLockoutPolicy(this.lockoutData.maxPasswordAttempts) .addCustomLockoutPolicy(this.lockoutData.maxPasswordAttempts)
.then(() => { .then(() => {
this.toast.showInfo('POLICY.TOAST.SET', true); this.toast.showInfo('POLICY.TOAST.SET', true);
@@ -125,7 +124,7 @@ export class PasswordLockoutPolicyComponent implements OnInit {
this.toast.showError(error); this.toast.showError(error);
}); });
} else { } else {
promise = this.service promise = (this.service as ManagementService)
.updateCustomLockoutPolicy(this.lockoutData.maxPasswordAttempts) .updateCustomLockoutPolicy(this.lockoutData.maxPasswordAttempts)
.then(() => { .then(() => {
this.toast.showInfo('POLICY.TOAST.SET', true); this.toast.showInfo('POLICY.TOAST.SET', true);
@@ -137,6 +136,7 @@ export class PasswordLockoutPolicyComponent implements OnInit {
} }
} }
} }
}
public get isDefault(): boolean { public get isDefault(): boolean {
if (this.lockoutData && this.serviceType === PolicyComponentServiceType.MGMT) { if (this.lockoutData && this.serviceType === PolicyComponentServiceType.MGMT) {

View File

@@ -2,11 +2,11 @@
[showSelectionActionButton]="showSelectionActionButton" [showSelectionActionButton]="showSelectionActionButton"
*ngIf="projectId" *ngIf="projectId"
(refreshed)="refreshPage()" (refreshed)="refreshPage()"
[dataSize]="dataSource?.totalResult ?? 0" [dataSize]="dataSource.totalResult"
[emitRefreshOnPreviousRoutes]="['/projects/' + projectId + '/roles/create']" [emitRefreshOnPreviousRoutes]="['/projects/' + projectId + '/roles/create']"
[selection]="selection" [selection]="selection"
[loading]="dataSource?.loading$ | async" [loading]="dataSource.loading$ | async"
[timestamp]="dataSource?.viewTimestamp" [timestamp]="dataSource.viewTimestamp"
> >
<ng-template cnslHasRole [hasRole]="['project.role.write:' + projectId, 'project.role.write']" actions> <ng-template cnslHasRole [hasRole]="['project.role.write:' + projectId, 'project.role.write']" actions>
<a <a
@@ -126,7 +126,7 @@
<cnsl-paginator <cnsl-paginator
#paginator #paginator
[timestamp]="dataSource?.viewTimestamp" [timestamp]="dataSource.viewTimestamp"
[length]="dataSource.totalResult" [length]="dataSource.totalResult"
[pageSize]="50" [pageSize]="50"
(page)="changePage()" (page)="changePage()"

View File

@@ -1,15 +1,19 @@
<div class="refresh-table-header-row" [ngClass]="{ 'border-bottom': showBorder }"> <div class="refresh-table-header-row" [ngClass]="{ 'border-bottom': showBorder }">
<div class="refresh-table-row" *ngIf="selection.hasValue(); else leftActions"> <div class="refresh-table-row" *ngIf="selection.hasValue(); else leftActions">
<div> <div>
<span class="count">{{selection?.selected?.length}}</span> <span class="count">{{ selection.selected.length }}</span>
<span class="count-desc">{{ 'ORG_DETAIL.TABLE.SELECTION' | translate }}</span> <span class="count-desc">{{ 'ORG_DETAIL.TABLE.SELECTION' | translate }}</span>
</div> </div>
<span class="count-slash">|</span> <span class="count-slash">|</span>
<a mat-stroked-button [ngClass]="{ 'cnsl-action-button': showSelectionActionButton }" (click)="selection.clear()"> <a mat-stroked-button [ngClass]="{ 'cnsl-action-button': showSelectionActionButton }" (click)="selection.clear()">
<span>{{ 'ORG_DETAIL.TABLE.CLEAR' | translate }}</span> <span>{{ 'ORG_DETAIL.TABLE.CLEAR' | translate }}</span>
<cnsl-action-keys *ngIf="showSelectionActionButton" [doNotUseContrast]="true" [type]="ActionKeysType.CLEAR" <cnsl-action-keys
(actionTriggered)="selection.clear()"> *ngIf="showSelectionActionButton"
[doNotUseContrast]="true"
[type]="ActionKeysType.CLEAR"
(actionTriggered)="selection.clear()"
>
</cnsl-action-keys> </cnsl-action-keys>
</a> </a>
</div> </div>
@@ -19,8 +23,13 @@
<span class="fill-space"></span> <span class="fill-space"></span>
<mat-spinner class="refresh-table-spinner" *ngIf="loading" diameter="20"></mat-spinner> <mat-spinner class="refresh-table-spinner" *ngIf="loading" diameter="20"></mat-spinner>
<button *ngIf="!hideRefresh" mat-icon-button (click)="emitRefresh()" class="refresh-table-icon-button" <button
matTooltip="{{'ACTIONS.REFRESH' | translate}}"> *ngIf="!hideRefresh"
mat-icon-button
(click)="emitRefresh()"
class="refresh-table-icon-button"
matTooltip="{{ 'ACTIONS.REFRESH' | translate }}"
>
<mat-icon class="icon">refresh</mat-icon> <mat-icon class="icon">refresh</mat-icon>
</button> </button>

View File

@@ -12,7 +12,7 @@
" "
class="avatar" class="avatar"
[name]="user.human.profile.displayName" [name]="user.human.profile.displayName"
[avatarUrl]="user.human.profile?.avatarUrl || ''" [avatarUrl]="user.human.profile.avatarUrl || ''"
[forColor]="user.preferredLoginName" [forColor]="user.preferredLoginName"
[size]="32" [size]="32"
> >
@@ -23,7 +23,7 @@
</div> </div>
<div class="user-name-column" *ngIf="user.human"> <div class="user-name-column" *ngIf="user.human">
<span>{{ user.human?.profile?.displayName }}</span> <span>{{ user.human.profile?.displayName }}</span>
<span class="smaller cnsl-secondary-text">{{ user.preferredLoginName }}</span> <span class="smaller cnsl-secondary-text">{{ user.preferredLoginName }}</span>
</div> </div>
<div class="user-name-column" *ngIf="user.machine"> <div class="user-name-column" *ngIf="user.machine">
@@ -85,7 +85,7 @@
" "
class="avatar" class="avatar"
[name]="user.human.profile.displayName" [name]="user.human.profile.displayName"
[avatarUrl]="user.human.profile?.avatarUrl || ''" [avatarUrl]="user.human.profile.avatarUrl || ''"
[forColor]="user.preferredLoginName" [forColor]="user.preferredLoginName"
[size]="32" [size]="32"
> >

View File

@@ -1,21 +1,12 @@
<cnsl-refresh-table <cnsl-refresh-table
[loading]="dataSource?.loading$ | async" [loading]="dataSource.loading$ | async"
(refreshed)="changePage()" (refreshed)="changePage()"
[hideRefresh]="true" [hideRefresh]="true"
[emitRefreshOnPreviousRoutes]="refreshOnPreviousRoutes" [emitRefreshOnPreviousRoutes]="refreshOnPreviousRoutes"
[timestamp]="dataSource?.viewTimestamp" [timestamp]="dataSource.viewTimestamp"
[dataSize]="dataSource?.totalResult ?? 0" [dataSize]="dataSource.totalResult"
[selection]="selection" [selection]="selection"
> >
<!-- <div leftActions class="user-grants-table-left-actions">
<button class="user-grant-type-button" [ngClass]="{'active': type === undefined}"
(click)="setType(undefined)">{{'PROJECT.GRANT.ALL' | translate}}</button>
<button class="user-grant-type-button" [ngClass]="{'active': type === Type.TYPE_HUMAN}"
(click)="setType(Type.TYPE_HUMAN)">{{'USER.TABLE.TYPES.HUMAN' | translate}}</button>
<button class="user-grant-type-button" [ngClass]="{'active': type === Type.TYPE_MACHINE}"
(click)="setType(Type.TYPE_MACHINE)">{{'USER.TABLE.TYPES.MACHINE' | translate}}</button>
</div> -->
<button <button
color="warn" color="warn"
matTooltip="{{ 'GRANTS.DELETE' | translate }}" matTooltip="{{ 'GRANTS.DELETE' | translate }}"
@@ -193,7 +184,7 @@
<cnsl-paginator <cnsl-paginator
class="paginator" class="paginator"
#paginator #paginator
[timestamp]="dataSource?.viewTimestamp" [timestamp]="dataSource.viewTimestamp"
[length]="dataSource.totalResult" [length]="dataSource.totalResult"
[pageSize]="INITIAL_PAGE_SIZE" [pageSize]="INITIAL_PAGE_SIZE"
[length]="dataSource.totalResult" [length]="dataSource.totalResult"

View File

@@ -38,10 +38,10 @@ export class UserGrantsComponent implements OnInit, AfterViewInit {
@Input() context: UserGrantContext = UserGrantContext.NONE; @Input() context: UserGrantContext = UserGrantContext.NONE;
@Input() refreshOnPreviousRoutes: string[] = []; @Input() refreshOnPreviousRoutes: string[] = [];
public dataSource!: UserGrantsDataSource; public dataSource: UserGrantsDataSource = new UserGrantsDataSource(this.userService);
public selection: SelectionModel<UserGrant.AsObject> = new SelectionModel<UserGrant.AsObject>(true, []); public selection: SelectionModel<UserGrant.AsObject> = new SelectionModel<UserGrant.AsObject>(true, []);
@ViewChild(PaginatorComponent) public paginator!: PaginatorComponent; @ViewChild(PaginatorComponent) public paginator?: PaginatorComponent;
@ViewChild(MatTable) public table!: MatTable<UserGrant.AsObject>; @ViewChild(MatTable) public table?: MatTable<UserGrant.AsObject>;
@Input() disableWrite: boolean = false; @Input() disableWrite: boolean = false;
@Input() disableDelete: boolean = false; @Input() disableDelete: boolean = false;
@@ -85,8 +85,6 @@ export class UserGrantsComponent implements OnInit, AfterViewInit {
]; ];
ngOnInit(): void { ngOnInit(): void {
this.dataSource = new UserGrantsDataSource(this.userService);
switch (this.context) { switch (this.context) {
case UserGrantContext.OWNED_PROJECT: case UserGrantContext.OWNED_PROJECT:
if (this.projectId) { if (this.projectId) {
@@ -113,7 +111,7 @@ export class UserGrantsComponent implements OnInit, AfterViewInit {
} }
public ngAfterViewInit(): void { public ngAfterViewInit(): void {
this.paginator.page.pipe(tap(() => this.loadGrantsPage(this.type))).subscribe(); this.paginator?.page.pipe(tap(() => this.loadGrantsPage(this.type))).subscribe();
} }
public setType(type: Type | undefined): void { public setType(type: Type | undefined): void {
@@ -271,8 +269,8 @@ export class UserGrantsComponent implements OnInit, AfterViewInit {
public changePage(event?: PageEvent): void { public changePage(event?: PageEvent): void {
this.dataSource.loadGrants( this.dataSource.loadGrants(
this.context, this.context,
event?.pageIndex ?? this.paginator.pageIndex, event?.pageIndex ?? this.paginator?.pageIndex ?? 0,
event?.pageSize ?? this.paginator.pageSize, event?.pageSize ?? this.paginator?.pageSize ?? this.INITIAL_PAGE_SIZE,
{ {
projectId: this.projectId, projectId: this.projectId,
grantId: this.grantId, grantId: this.grantId,

View File

@@ -2,7 +2,7 @@
[hideRefresh]="true" [hideRefresh]="true"
[loading]="loading$ | async" [loading]="loading$ | async"
(refreshed)="refreshPage()" (refreshed)="refreshPage()"
[dataSize]="dataSource?.data?.length ?? 0" [dataSize]="dataSource.data.length"
[timestamp]="actionsResult?.details?.viewTimestamp" [timestamp]="actionsResult?.details?.viewTimestamp"
[selection]="selection" [selection]="selection"
> >

View File

@@ -28,7 +28,7 @@ export class ActionTableComponent implements OnInit {
@ViewChild(PaginatorComponent) public paginator!: PaginatorComponent; @ViewChild(PaginatorComponent) public paginator!: PaginatorComponent;
public dataSource: MatTableDataSource<Action.AsObject> = new MatTableDataSource<Action.AsObject>(); public dataSource: MatTableDataSource<Action.AsObject> = new MatTableDataSource<Action.AsObject>();
public selection: SelectionModel<Action.AsObject> = new SelectionModel<Action.AsObject>(true, []); public selection: SelectionModel<Action.AsObject> = new SelectionModel<Action.AsObject>(true, []);
public actionsResult!: ListActionsResponse.AsObject; public actionsResult?: ListActionsResponse.AsObject;
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false); private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
public loading$: Observable<boolean> = this.loadingSubject.asObservable(); public loading$: Observable<boolean> = this.loadingSubject.asObservable();
@Input() public displayedColumns: string[] = ['select', 'name', 'state', 'timeout', 'allowedToFail', 'actions']; @Input() public displayedColumns: string[] = ['select', 'name', 'state', 'timeout', 'allowedToFail', 'actions'];
@@ -50,7 +50,7 @@ export class ActionTableComponent implements OnInit {
} }
ngOnInit(): void { ngOnInit(): void {
this.getData(10, 0); this.getData(20, 0);
} }
public isAllSelected(): boolean { public isAllSelected(): boolean {
@@ -84,7 +84,7 @@ export class ActionTableComponent implements OnInit {
.deleteAction(action.id) .deleteAction(action.id)
.then(() => { .then(() => {
this.toast.showInfo('FLOWS.DIALOG.DELETEACTION.DELETE_SUCCESS', true); this.toast.showInfo('FLOWS.DIALOG.DELETEACTION.DELETE_SUCCESS', true);
this.getData(10, 0); this.getData(20, 0);
}) })
.catch((error: any) => { .catch((error: any) => {
this.toast.showError(error); this.toast.showError(error);

View File

@@ -1,4 +1,4 @@
<span class="title" mat-dialog-title>{{ 'ORG.PAGES.ORGDOMAIN.TITLE' | translate }} {{ domain.domainName }}</span> <span class="title" mat-dialog-title>{{ 'ORG.PAGES.ORGDOMAIN.TITLE' | translate }} {{ domain?.domainName }}</span>
<div mat-dialog-content> <div mat-dialog-content>
<p class="desc">{{ 'ORG.PAGES.ORGDOMAIN.VERIFICATION' | translate }}</p> <p class="desc">{{ 'ORG.PAGES.ORGDOMAIN.VERIFICATION' | translate }}</p>
@@ -7,17 +7,17 @@
}}</cnsl-info-section> }}</cnsl-info-section>
<p <p
*ngIf="domain.validationType !== DomainValidationType.DOMAIN_VALIDATION_TYPE_UNSPECIFIED && !(dns || http)" *ngIf="domain?.validationType !== DomainValidationType.DOMAIN_VALIDATION_TYPE_UNSPECIFIED && !(dns || http)"
class="desc" class="desc"
> >
{{ 'ORG.PAGES.ORGDOMAIN.VERIFICATION_VALIDATION_ONGOING' | translate: domain }} {{ 'ORG.PAGES.ORGDOMAIN.VERIFICATION_VALIDATION_ONGOING' | translate: domain }}
{{ 'ORG.PAGES.ORGDOMAIN.VERIFICATION_VALIDATION_ONGOING_TYPE' | translate }} {{ 'ORG.PAGES.ORGDOMAIN.VERIFICATION_VALIDATION_ONGOING_TYPE' | translate }}
{{ 'ORG.PAGES.ORGDOMAIN.TYPES.' + domain.validationType | translate }} {{ 'ORG.PAGES.ORGDOMAIN.TYPES.' + domain?.validationType | translate }}
</p> </p>
<div class="btn-container"> <div class="btn-container">
<button <button
[disabled]="domain.validationType === DomainValidationType.DOMAIN_VALIDATION_TYPE_UNSPECIFIED" [disabled]="domain?.validationType === DomainValidationType.DOMAIN_VALIDATION_TYPE_UNSPECIFIED"
color="primary" color="primary"
type="submit" type="submit"
mat-raised-button mat-raised-button
@@ -44,7 +44,7 @@
<div *ngIf="http"> <div *ngIf="http">
<p>HTTP TOKEN</p> <p>HTTP TOKEN</p>
<p class="entry">{{ http?.url }}.txt</p> <p class="entry">{{ http.url }}.txt</p>
<div class="btn-container"> <div class="btn-container">
<button mat-stroked-button (click)="saveFile()" color="primary">{{ 'ORG.PAGES.DOWNLOAD_FILE' | translate }}</button> <button mat-stroked-button (click)="saveFile()" color="primary">{{ 'ORG.PAGES.DOWNLOAD_FILE' | translate }}</button>
@@ -57,8 +57,8 @@
<div *ngIf="dns"> <div *ngIf="dns">
<p>DNS TOKEN</p> <p>DNS TOKEN</p>
<div class="domain-line" *ngIf="dns?.token"> <div class="domain-line" *ngIf="dns.token">
<p class="entry">{{ dns?.token }}</p> <p class="entry">{{ dns.token }}</p>
<button <button
color="primary" color="primary"
[disabled]="copied === data.clientSecret" [disabled]="copied === data.clientSecret"
@@ -76,7 +76,7 @@
</button> </button>
<mat-spinner class="spinner" *ngIf="validating" diameter="20" mode="indeterminate"></mat-spinner> <mat-spinner class="spinner" *ngIf="validating" diameter="20" mode="indeterminate"></mat-spinner>
</div> </div>
<p class="entry">{{ dns?.url }}</p> <p class="entry">{{ dns.url }}</p>
</div> </div>
</ng-container> </ng-container>
</div> </div>

View File

@@ -13,12 +13,12 @@ import { ToastService } from 'src/app/services/toast.service';
styleUrls: ['./domain-verification.component.scss'], styleUrls: ['./domain-verification.component.scss'],
}) })
export class DomainVerificationComponent { export class DomainVerificationComponent {
public domain!: Domain.AsObject; public domain?: Domain.AsObject;
public DomainValidationType: any = DomainValidationType; public DomainValidationType: any = DomainValidationType;
public http!: GenerateOrgDomainValidationResponse.AsObject; public http?: GenerateOrgDomainValidationResponse.AsObject;
public dns!: GenerateOrgDomainValidationResponse.AsObject; public dns?: GenerateOrgDomainValidationResponse.AsObject;
public copied: string = ''; public copied: string = '';
@@ -34,26 +34,31 @@ export class DomainVerificationComponent {
private mgmtService: ManagementService, private mgmtService: ManagementService,
) { ) {
this.domain = data.domain; this.domain = data.domain;
if (this.domain.validationType === DomainValidationType.DOMAIN_VALIDATION_TYPE_UNSPECIFIED) {
if (this.domain?.validationType === DomainValidationType.DOMAIN_VALIDATION_TYPE_UNSPECIFIED) {
this.showNew = true; this.showNew = true;
} }
} }
async loadHttpToken(): Promise<void> { async loadHttpToken(): Promise<void> {
if (this.domain) {
this.mgmtService this.mgmtService
.generateOrgDomainValidation(this.domain.domainName, DomainValidationType.DOMAIN_VALIDATION_TYPE_HTTP) .generateOrgDomainValidation(this.domain.domainName, DomainValidationType.DOMAIN_VALIDATION_TYPE_HTTP)
.then((http) => { .then((http) => {
this.http = http; this.http = http;
}); });
} }
}
async loadDnsToken(): Promise<void> { async loadDnsToken(): Promise<void> {
if (this.domain) {
this.mgmtService this.mgmtService
.generateOrgDomainValidation(this.domain.domainName, DomainValidationType.DOMAIN_VALIDATION_TYPE_DNS) .generateOrgDomainValidation(this.domain.domainName, DomainValidationType.DOMAIN_VALIDATION_TYPE_DNS)
.then((dns) => { .then((dns) => {
this.dns = dns; this.dns = dns;
}); });
} }
}
public closeDialog(): void { public closeDialog(): void {
this.dialogRef.close(false); this.dialogRef.close(false);
@@ -61,6 +66,7 @@ export class DomainVerificationComponent {
public validate(): void { public validate(): void {
this.validating = true; this.validating = true;
if (this.domain) {
this.mgmtService this.mgmtService
.validateOrgDomain(this.domain.domainName) .validateOrgDomain(this.domain.domainName)
.then(() => { .then(() => {
@@ -73,9 +79,12 @@ export class DomainVerificationComponent {
this.validating = false; this.validating = false;
}); });
} }
}
public saveFile(): void { public saveFile(): void {
if (this.http) {
const blob = new Blob([this.http.token], { type: 'text/plain;charset=utf-8' }); const blob = new Blob([this.http.token], { type: 'text/plain;charset=utf-8' });
saveAs(blob, this.http.token + '.txt'); saveAs(blob, this.http.token + '.txt');
} }
} }
}

View File

@@ -18,7 +18,7 @@ import { ToastService } from 'src/app/services/toast.service';
styleUrls: ['./instance.component.scss'], styleUrls: ['./instance.component.scss'],
}) })
export class InstanceComponent { export class InstanceComponent {
public instance!: InstanceDetail.AsObject; public instance?: InstanceDetail.AsObject;
public PolicyComponentServiceType: any = PolicyComponentServiceType; public PolicyComponentServiceType: any = PolicyComponentServiceType;
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false); private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
public loading$: Observable<boolean> = this.loadingSubject.asObservable(); public loading$: Observable<boolean> = this.loadingSubject.asObservable();

View File

@@ -51,7 +51,7 @@
<h1>{{ 'ORG.PAGES.ORGDETAILUSER_TITLE' | translate }}</h1> <h1>{{ 'ORG.PAGES.ORGDETAILUSER_TITLE' | translate }}</h1>
<div class="user"> <div class="user">
<form [formGroup]="userForm" class="form"> <form [formGroup]="userForm" *ngIf="userForm" class="form">
<div class="content"> <div class="content">
<p class="section cnsl-secondary-text">{{ 'USER.CREATE.NAMEANDEMAILSECTION' | translate }}</p> <p class="section cnsl-secondary-text">{{ 'USER.CREATE.NAMEANDEMAILSECTION' | translate }}</p>
<cnsl-form-field class="formfield"> <cnsl-form-field class="formfield">
@@ -136,7 +136,12 @@
<ng-container *ngIf="usePassword && pwdForm"> <ng-container *ngIf="usePassword && pwdForm">
<p class="section cnsl-secondary-text">{{ 'USER.CREATE.PASSWORDSECTION' | translate }}</p> <p class="section cnsl-secondary-text">{{ 'USER.CREATE.PASSWORDSECTION' | translate }}</p>
<cnsl-password-complexity-view class="complexity-view" [policy]="this.policy" [password]="password"> <cnsl-password-complexity-view
*ngIf="policy"
class="complexity-view"
[policy]="policy"
[password]="password"
>
</cnsl-password-complexity-view> </cnsl-password-complexity-view>
<form [formGroup]="pwdForm" class="pwd-form"> <form [formGroup]="pwdForm" class="pwd-form">
@@ -177,7 +182,7 @@
color="primary" color="primary"
class="big-button" class="big-button"
(click)="finish()" (click)="finish()"
[disabled]="orgForm.invalid || userForm.invalid || (usePassword && pwdForm ? pwdForm?.invalid : false)" [disabled]="orgForm.invalid || userForm.invalid || (usePassword && pwdForm ? pwdForm.invalid : false)"
mat-raised-button mat-raised-button
> >
{{ 'ACTIONS.FINISH' | translate }} {{ 'ACTIONS.FINISH' | translate }}

View File

@@ -50,14 +50,18 @@ function passwordConfirmValidator(c: AbstractControl): any {
], ],
}) })
export class OrgCreateComponent { export class OrgCreateComponent {
public orgForm!: UntypedFormGroup; public orgForm: UntypedFormGroup = this.fb.group({
public userForm!: UntypedFormGroup; name: ['', [Validators.required]],
public pwdForm!: UntypedFormGroup; domain: [''],
});
public userForm?: UntypedFormGroup;
public pwdForm?: UntypedFormGroup;
public genders: Gender[] = [Gender.GENDER_FEMALE, Gender.GENDER_MALE, Gender.GENDER_UNSPECIFIED]; public genders: Gender[] = [Gender.GENDER_FEMALE, Gender.GENDER_MALE, Gender.GENDER_UNSPECIFIED];
public languages: string[] = ['de', 'en']; public languages: string[] = ['de', 'en', 'it', 'fr'];
public policy!: PasswordComplexityPolicy.AsObject; public policy?: PasswordComplexityPolicy.AsObject;
public usePassword: boolean = false; public usePassword: boolean = false;
public forSelf: boolean = true; public forSelf: boolean = true;
@@ -70,7 +74,7 @@ export class OrgCreateComponent {
private fb: UntypedFormBuilder, private fb: UntypedFormBuilder,
private mgmtService: ManagementService, private mgmtService: ManagementService,
private authService: GrpcAuthService, private authService: GrpcAuthService,
private breadcrumbService: BreadcrumbService, breadcrumbService: BreadcrumbService,
) { ) {
const instanceBread = new Breadcrumb({ const instanceBread = new Breadcrumb({
type: BreadcrumbType.INSTANCE, type: BreadcrumbType.INSTANCE,
@@ -89,12 +93,11 @@ export class OrgCreateComponent {
} }
}); });
this.orgForm = this.fb.group({
name: ['', [Validators.required]],
domain: [''],
});
this.initForm(); this.initForm();
this.adminService.getSupportedLanguages().then((supportedResp) => {
this.languages = supportedResp.languagesList;
});
} }
public createSteps: number = 2; public createSteps: number = 2;
@@ -125,16 +128,8 @@ export class OrgCreateComponent {
this.adminService this.adminService
.SetUpOrg(createOrgRequest, humanRequest) .SetUpOrg(createOrgRequest, humanRequest)
.then((resp) => { .then(() => {
this.router.navigate(['/orgs']); this.router.navigate(['/orgs']);
// const orgResp = org.getOrg();
// if (orgResp) {
// this.authService.setActiveOrg(orgResp.toObject());
// this.router.navigate(['/org']);
// } else {
// this.router.navigate(['/org', 'overview']);
// }
}) })
.catch((error) => { .catch((error) => {
this.toast.showError(error); this.toast.showError(error);
@@ -241,43 +236,43 @@ export class OrgCreateComponent {
} }
public get userName(): AbstractControl | null { public get userName(): AbstractControl | null {
return this.userForm.get('userName'); return this.userForm?.get('userName') ?? null;
} }
public get firstName(): AbstractControl | null { public get firstName(): AbstractControl | null {
return this.userForm.get('firstName'); return this.userForm?.get('firstName') ?? null;
} }
public get lastName(): AbstractControl | null { public get lastName(): AbstractControl | null {
return this.userForm.get('lastName'); return this.userForm?.get('lastName') ?? null;
} }
public get email(): AbstractControl | null { public get email(): AbstractControl | null {
return this.userForm.get('email'); return this.userForm?.get('email') ?? null;
} }
public get isVerified(): AbstractControl | null { public get isVerified(): AbstractControl | null {
return this.userForm.get('isVerified'); return this.userForm?.get('isVerified') ?? null;
} }
public get nickName(): AbstractControl | null { public get nickName(): AbstractControl | null {
return this.userForm.get('nickName'); return this.userForm?.get('nickName') ?? null;
} }
public get preferredLanguage(): AbstractControl | null { public get preferredLanguage(): AbstractControl | null {
return this.userForm.get('preferredLanguage'); return this.userForm?.get('preferredLanguage') ?? null;
} }
public get gender(): AbstractControl | null { public get gender(): AbstractControl | null {
return this.userForm.get('gender'); return this.userForm?.get('gender') ?? null;
} }
public get password(): AbstractControl | null { public get password(): AbstractControl | null {
return this.pwdForm.get('password'); return this.pwdForm?.get('password') ?? null;
} }
public get confirmPassword(): AbstractControl | null { public get confirmPassword(): AbstractControl | null {
return this.pwdForm.get('confirmPassword'); return this.pwdForm?.get('confirmPassword') ?? null;
} }
public close(): void { public close(): void {

View File

@@ -21,7 +21,7 @@ import { ToastService } from 'src/app/services/toast.service';
styleUrls: ['./org-detail.component.scss'], styleUrls: ['./org-detail.component.scss'],
}) })
export class OrgDetailComponent implements OnInit, OnDestroy { export class OrgDetailComponent implements OnInit, OnDestroy {
public org!: Org.AsObject; public org?: Org.AsObject;
public PolicyComponentServiceType: any = PolicyComponentServiceType; public PolicyComponentServiceType: any = PolicyComponentServiceType;
public OrgState: any = OrgState; public OrgState: any = OrgState;
@@ -80,7 +80,7 @@ export class OrgDetailComponent implements OnInit, OnDestroy {
.reactivateOrg() .reactivateOrg()
.then(() => { .then(() => {
this.toast.showInfo('ORG.TOAST.REACTIVATED', true); this.toast.showInfo('ORG.TOAST.REACTIVATED', true);
this.org.state = OrgState.ORG_STATE_ACTIVE; this.org!.state = OrgState.ORG_STATE_ACTIVE;
}) })
.catch((error) => { .catch((error) => {
this.toast.showError(error); this.toast.showError(error);
@@ -103,7 +103,7 @@ export class OrgDetailComponent implements OnInit, OnDestroy {
.deactivateOrg() .deactivateOrg()
.then(() => { .then(() => {
this.toast.showInfo('ORG.TOAST.DEACTIVATED', true); this.toast.showInfo('ORG.TOAST.DEACTIVATED', true);
this.org.state = OrgState.ORG_STATE_INACTIVE; this.org!.state = OrgState.ORG_STATE_INACTIVE;
}) })
.catch((error) => { .catch((error) => {
this.toast.showError(error); this.toast.showError(error);

View File

@@ -19,7 +19,7 @@ import { OrgMembersDataSource } from './org-members-datasource';
}) })
export class OrgMembersComponent { export class OrgMembersComponent {
public INITIALPAGESIZE: number = 25; public INITIALPAGESIZE: number = 25;
public org!: Org.AsObject; public org?: Org.AsObject;
public disableWrite: boolean = false; public disableWrite: boolean = false;
public dataSource!: OrgMembersDataSource; public dataSource!: OrgMembersDataSource;

View File

@@ -186,7 +186,7 @@
</span> </span>
<span class="right"> <span class="right">
<span> <span>
{{ 'APP.OIDC.AUTHMETHOD.' + oidcAppRequest?.authMethodType | translate }} {{ 'APP.OIDC.AUTHMETHOD.' + oidcAppRequest.authMethodType | translate }}
</span> </span>
</span> </span>
</div> </div>
@@ -226,7 +226,7 @@
</span> </span>
<span class="right"> <span class="right">
<span> <span>
{{ 'APP.API.AUTHMETHOD.' + apiAppRequest?.authMethodType | translate }} {{ 'APP.API.AUTHMETHOD.' + apiAppRequest.authMethodType | translate }}
</span> </span>
</span> </span>
</div> </div>

View File

@@ -43,7 +43,7 @@ import { API_TYPE, AppCreateType, NATIVE_TYPE, RadioItemAppType, USER_AGENT_TYPE
styleUrls: ['./app-create.component.scss'], styleUrls: ['./app-create.component.scss'],
}) })
export class AppCreateComponent implements OnInit, OnDestroy { export class AppCreateComponent implements OnInit, OnDestroy {
private subscription?: Subscription; private subscription: Subscription = new Subscription();
private destroyed$: Subject<void> = new Subject(); private destroyed$: Subject<void> = new Subject();
public devmode: boolean = false; public devmode: boolean = false;
public projectId: string = ''; public projectId: string = '';

View File

@@ -57,7 +57,10 @@
</cnsl-top-view> </cnsl-top-view>
<div class="max-width-container"> <div class="max-width-container">
<div class="compliance" *ngIf="app?.oidcConfig?.complianceProblemsList && app.oidcConfig?.complianceProblemsList?.length"> <div
class="compliance"
*ngIf="app && app.oidcConfig && app.oidcConfig.complianceProblemsList && app.oidcConfig.complianceProblemsList?.length"
>
<h2 class="compliance-title">{{ 'APP.COMPLIANCE' | translate }}</h2> <h2 class="compliance-title">{{ 'APP.COMPLIANCE' | translate }}</h2>
<cnsl-info-section class="problem" [type]="InfoSectionType.ALERT"> <cnsl-info-section class="problem" [type]="InfoSectionType.ALERT">
<ul style="margin: 0"> <ul style="margin: 0">
@@ -355,7 +358,7 @@
<ng-container *ngIf="currentSetting === 'configuration'"> <ng-container *ngIf="currentSetting === 'configuration'">
<cnsl-card <cnsl-card
*ngIf="initialAuthMethod === 'PK_JWT' && projectId && app?.id" *ngIf="initialAuthMethod === 'PK_JWT' && projectId && app && app.id"
title="{{ 'USER.MACHINE.KEYSTITLE' | translate }}" title="{{ 'USER.MACHINE.KEYSTITLE' | translate }}"
description="{{ 'USER.MACHINE.KEYSDESC' | translate }}" description="{{ 'USER.MACHINE.KEYSDESC' | translate }}"
> >

View File

@@ -71,7 +71,7 @@ export class AppDetailComponent implements OnInit, OnDestroy {
public authMethods: RadioItemAuthType[] = []; public authMethods: RadioItemAuthType[] = [];
private subscription?: Subscription; private subscription?: Subscription;
public projectId: string = ''; public projectId: string = '';
public app!: App.AsObject; public app?: App.AsObject;
public environmentMap: { [key: string]: string } = {}; public environmentMap: { [key: string]: string } = {};
@@ -183,7 +183,7 @@ export class AppDetailComponent implements OnInit, OnDestroy {
public openNameDialog(): void { public openNameDialog(): void {
const dialogRef = this.dialog.open(NameDialogComponent, { const dialogRef = this.dialog.open(NameDialogComponent, {
data: { data: {
name: this.app.name, name: this.app?.name,
titleKey: 'APP.NAMEDIALOG.TITLE', titleKey: 'APP.NAMEDIALOG.TITLE',
descKey: 'APP.NAMEDIALOG.DESCRIPTION', descKey: 'APP.NAMEDIALOG.DESCRIPTION',
labelKey: 'APP.NAMEDIALOG.NAME', labelKey: 'APP.NAMEDIALOG.NAME',
@@ -193,7 +193,7 @@ export class AppDetailComponent implements OnInit, OnDestroy {
dialogRef.afterClosed().subscribe((name) => { dialogRef.afterClosed().subscribe((name) => {
if (name) { if (name) {
this.app.name = name; this.app!.name = name;
this.saveApp(); this.saveApp();
} }
}); });
@@ -344,7 +344,6 @@ export class AppDetailComponent implements OnInit, OnDestroy {
} }
}) })
.catch((error) => { .catch((error) => {
console.error(error);
this.toast.showError(error); this.toast.showError(error);
this.errorMessage = error.message; this.errorMessage = error.message;
}); });
@@ -354,7 +353,7 @@ export class AppDetailComponent implements OnInit, OnDestroy {
private getAuthMethodOptions(type: string): void { private getAuthMethodOptions(type: string): void {
if (type === 'OIDC') { if (type === 'OIDC') {
switch (this.app.oidcConfig?.appType) { switch (this.app?.oidcConfig?.appType) {
case OIDCAppType.OIDC_APP_TYPE_NATIVE: case OIDCAppType.OIDC_APP_TYPE_NATIVE:
this.authMethods = [PKCE_METHOD, CUSTOM_METHOD]; this.authMethods = [PKCE_METHOD, CUSTOM_METHOD];
break; break;
@@ -378,17 +377,17 @@ export class AppDetailComponent implements OnInit, OnDestroy {
public setPartialConfigFromAuthMethod(authMethod: string): void { public setPartialConfigFromAuthMethod(authMethod: string): void {
const partialConfig = getPartialConfigFromAuthMethod(authMethod); const partialConfig = getPartialConfigFromAuthMethod(authMethod);
if (partialConfig && partialConfig.oidc && this.app.oidcConfig) { if (partialConfig && partialConfig.oidc && this.app?.oidcConfig) {
this.app.oidcConfig.responseTypesList = (partialConfig.oidc as Partial<OIDCConfig.AsObject>).responseTypesList ?? []; this.app!.oidcConfig.responseTypesList = (partialConfig.oidc as Partial<OIDCConfig.AsObject>).responseTypesList ?? [];
this.app.oidcConfig.grantTypesList = (partialConfig.oidc as Partial<OIDCConfig.AsObject>).grantTypesList ?? []; this.app!.oidcConfig.grantTypesList = (partialConfig.oidc as Partial<OIDCConfig.AsObject>).grantTypesList ?? [];
this.app.oidcConfig.authMethodType = this.app!.oidcConfig.authMethodType =
(partialConfig.oidc as Partial<OIDCConfig.AsObject>).authMethodType ?? OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE; (partialConfig.oidc as Partial<OIDCConfig.AsObject>).authMethodType ?? OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE;
this.oidcForm.patchValue(this.app.oidcConfig); this.oidcForm.patchValue(this.app.oidcConfig);
this.oidcTokenForm.patchValue(this.app.oidcConfig); this.oidcTokenForm.patchValue(this.app.oidcConfig);
} else if (partialConfig && partialConfig.api && this.app.apiConfig) { } else if (partialConfig && partialConfig.api && this.app?.apiConfig) {
this.app.apiConfig.authMethodType = this.app.apiConfig.authMethodType =
(partialConfig.api as Partial<APIConfig.AsObject>).authMethodType ?? APIAuthMethodType.API_AUTH_METHOD_TYPE_BASIC; (partialConfig.api as Partial<APIConfig.AsObject>).authMethodType ?? APIAuthMethodType.API_AUTH_METHOD_TYPE_BASIC;
@@ -408,7 +407,7 @@ export class AppDetailComponent implements OnInit, OnDestroy {
}); });
dialogRef.afterClosed().subscribe((resp) => { dialogRef.afterClosed().subscribe((resp) => {
if (resp && this.projectId && this.app.id) { if (resp && this.projectId && this.app?.id) {
this.mgmtService this.mgmtService
.removeApp(this.projectId, this.app.id) .removeApp(this.projectId, this.app.id)
.then(() => { .then(() => {
@@ -424,21 +423,21 @@ export class AppDetailComponent implements OnInit, OnDestroy {
} }
public changeState(state: AppState): void { public changeState(state: AppState): void {
if (state === AppState.APP_STATE_ACTIVE) { if (state === AppState.APP_STATE_ACTIVE && this.app) {
this.mgmtService this.mgmtService
.reactivateApp(this.projectId, this.app.id) .reactivateApp(this.projectId, this.app.id)
.then(() => { .then(() => {
this.app.state = state; this.app!.state = state;
this.toast.showInfo('APP.TOAST.REACTIVATED', true); this.toast.showInfo('APP.TOAST.REACTIVATED', true);
}) })
.catch((error: any) => { .catch((error: any) => {
this.toast.showError(error); this.toast.showError(error);
}); });
} else if (state === AppState.APP_STATE_INACTIVE) { } else if (state === AppState.APP_STATE_INACTIVE && this.app) {
this.mgmtService this.mgmtService
.deactivateApp(this.projectId, this.app.id) .deactivateApp(this.projectId, this.app.id)
.then(() => { .then(() => {
this.app.state = state; this.app!.state = state;
this.toast.showInfo('APP.TOAST.DEACTIVATED', true); this.toast.showInfo('APP.TOAST.DEACTIVATED', true);
}) })
.catch((error: any) => { .catch((error: any) => {
@@ -448,6 +447,7 @@ export class AppDetailComponent implements OnInit, OnDestroy {
} }
public saveApp(): void { public saveApp(): void {
if (this.app) {
this.mgmtService this.mgmtService
.updateApp(this.projectId, this.app.id, this.app.name) .updateApp(this.projectId, this.app.id, this.app.name)
.then(() => { .then(() => {
@@ -458,6 +458,7 @@ export class AppDetailComponent implements OnInit, OnDestroy {
this.toast.showError(error); this.toast.showError(error);
}); });
} }
}
public toggleRefreshToken(event: MatCheckboxChange): void { public toggleRefreshToken(event: MatCheckboxChange): void {
const c = this.grantTypesList?.value; const c = this.grantTypesList?.value;
@@ -481,7 +482,7 @@ export class AppDetailComponent implements OnInit, OnDestroy {
public saveOIDCApp(): void { public saveOIDCApp(): void {
this.requestRedirectValuesSubject$.next(); this.requestRedirectValuesSubject$.next();
if (this.oidcForm.valid) { if (this.oidcForm.valid) {
if (this.app.oidcConfig) { if (this.app?.oidcConfig) {
// configuration // configuration
this.app.oidcConfig.responseTypesList = this.responseTypesList?.value; this.app.oidcConfig.responseTypesList = this.responseTypesList?.value;
this.app.oidcConfig.grantTypesList = this.grantTypesList?.value; this.app.oidcConfig.grantTypesList = this.grantTypesList?.value;
@@ -532,7 +533,7 @@ export class AppDetailComponent implements OnInit, OnDestroy {
this.mgmtService this.mgmtService
.updateOIDCAppConfig(req) .updateOIDCAppConfig(req)
.then(() => { .then(() => {
if (this.app.oidcConfig) { if (this.app?.oidcConfig) {
const config = { oidc: this.app.oidcConfig }; const config = { oidc: this.app.oidcConfig };
this.currentAuthMethod = this.authMethodFromPartialConfig(config); this.currentAuthMethod = this.authMethodFromPartialConfig(config);
} }
@@ -546,7 +547,7 @@ export class AppDetailComponent implements OnInit, OnDestroy {
} }
public saveAPIApp(): void { public saveAPIApp(): void {
if (this.apiForm.valid && this.app.apiConfig) { if (this.apiForm.valid && this.app?.apiConfig) {
this.app.apiConfig.authMethodType = this.apiAuthMethodType?.value; this.app.apiConfig.authMethodType = this.apiAuthMethodType?.value;
const req = new UpdateAPIAppConfigRequest(); const req = new UpdateAPIAppConfigRequest();
@@ -557,7 +558,7 @@ export class AppDetailComponent implements OnInit, OnDestroy {
this.mgmtService this.mgmtService
.updateAPIAppConfig(req) .updateAPIAppConfig(req)
.then(() => { .then(() => {
if (this.app.apiConfig) { if (this.app?.apiConfig) {
const config = { api: this.app.apiConfig }; const config = { api: this.app.apiConfig };
this.currentAuthMethod = this.authMethodFromPartialConfig(config); this.currentAuthMethod = this.authMethodFromPartialConfig(config);
@@ -581,6 +582,7 @@ export class AppDetailComponent implements OnInit, OnDestroy {
} }
public regenerateOIDCClientSecret(): void { public regenerateOIDCClientSecret(): void {
if (this.app) {
this.mgmtService this.mgmtService
.regenerateOIDCClientSecret(this.app.id, this.projectId) .regenerateOIDCClientSecret(this.app.id, this.projectId)
.then((resp) => { .then((resp) => {
@@ -597,6 +599,7 @@ export class AppDetailComponent implements OnInit, OnDestroy {
this.toast.showError(error); this.toast.showError(error);
}); });
} }
}
public changeAuthMethod(): void { public changeAuthMethod(): void {
const ref = this.dialog.open(AuthMethodDialogComponent, { const ref = this.dialog.open(AuthMethodDialogComponent, {
@@ -617,6 +620,7 @@ export class AppDetailComponent implements OnInit, OnDestroy {
} }
public regenerateAPIClientSecret(): void { public regenerateAPIClientSecret(): void {
if (this.app) {
this.mgmtService this.mgmtService
.regenerateAPIClientSecret(this.app.id, this.projectId) .regenerateAPIClientSecret(this.app.id, this.projectId)
.then((resp) => { .then((resp) => {
@@ -632,6 +636,7 @@ export class AppDetailComponent implements OnInit, OnDestroy {
this.toast.showError(error); this.toast.showError(error);
}); });
} }
}
public get currentRadioItemAuthType(): RadioItemAuthType | undefined { public get currentRadioItemAuthType(): RadioItemAuthType | undefined {
return this.authMethods.find((i) => i.key === this.initialAuthMethod); return this.authMethods.find((i) => i.key === this.initialAuthMethod);

View File

@@ -54,7 +54,12 @@
</ng-template> </ng-template>
<div metainfo> <div metainfo>
<cnsl-changes *ngIf="project" [changeType]="ChangeType.PROJECT_GRANT" [id]="project.projectId" [secId]="project.grantId"></cnsl-changes> <cnsl-changes
*ngIf="project"
[changeType]="ChangeType.PROJECT_GRANT"
[id]="project.projectId"
[secId]="project.grantId"
></cnsl-changes>
</div> </div>
</cnsl-meta-layout> </cnsl-meta-layout>
</div> </div>

View File

@@ -23,16 +23,12 @@ import { ToastService } from 'src/app/services/toast.service';
export class GrantedProjectDetailComponent implements OnInit, OnDestroy { export class GrantedProjectDetailComponent implements OnInit, OnDestroy {
public projectId: string = ''; public projectId: string = '';
public grantId: string = ''; public grantId: string = '';
public project!: GrantedProject.AsObject; public project?: GrantedProject.AsObject;
public ProjectGrantState: any = ProjectGrantState; public ProjectGrantState: any = ProjectGrantState;
public ChangeType: any = ChangeType; public ChangeType: any = ChangeType;
private subscription?: Subscription;
public isZitadel: boolean = false; public isZitadel: boolean = false;
public UserGrantContext: any = UserGrantContext;
UserGrantContext: any = UserGrantContext; private subscription: Subscription = new Subscription();
// members // members
public totalMemberResult: number = 0; public totalMemberResult: number = 0;
@@ -56,7 +52,7 @@ export class GrantedProjectDetailComponent implements OnInit, OnDestroy {
} }
public ngOnDestroy(): void { public ngOnDestroy(): void {
this.subscription?.unsubscribe(); this.subscription.unsubscribe();
} }
private async getData({ id, grantId }: Params): Promise<void> { private async getData({ id, grantId }: Params): Promise<void> {
@@ -155,6 +151,8 @@ export class GrantedProjectDetailComponent implements OnInit, OnDestroy {
} }
public showDetail(): void { public showDetail(): void {
if (this.project) {
this.router.navigate(['granted-projects', this.project.projectId, 'grant', this.grantId, 'members']); this.router.navigate(['granted-projects', this.project.projectId, 'grant', this.grantId, 'members']);
} }
} }
}

View File

@@ -1,8 +1,12 @@
<cnsl-refresh-table [loading]="dataSource.loading$ | async" [selection]="selection" (refreshed)="refreshPage()" <cnsl-refresh-table
[dataSize]="dataSource.totalResult" [timestamp]="dataSource?.viewTimestamp"> [loading]="dataSource.loading$ | async"
[selection]="selection"
(refreshed)="refreshPage()"
[dataSize]="dataSource.totalResult"
[timestamp]="dataSource.viewTimestamp"
>
<ng-template cnslHasRole [hasRole]="['project.app.write']" actions> <ng-template cnslHasRole [hasRole]="['project.app.write']" actions>
<a [disabled]="disabled" [routerLink]="[ '/projects', projectId, 'apps', 'create']" color="primary" <a [disabled]="disabled" [routerLink]="['/projects', projectId, 'apps', 'create']" color="primary" mat-raised-button>
mat-raised-button>
<mat-icon class="icon">add</mat-icon>{{ 'ACTIONS.NEW' | translate }} <mat-icon class="icon">add</mat-icon>{{ 'ACTIONS.NEW' | translate }}
</a> </a>
</ng-template> </ng-template>
@@ -11,14 +15,21 @@
<table [dataSource]="dataSource" mat-table class="table" aria-label="Elements"> <table [dataSource]="dataSource" mat-table class="table" aria-label="Elements">
<ng-container matColumnDef="select"> <ng-container matColumnDef="select">
<th class="selection" mat-header-cell *matHeaderCellDef> <th class="selection" mat-header-cell *matHeaderCellDef>
<mat-checkbox color="primary" (change)="$event ? masterToggle() : null" <mat-checkbox
color="primary"
(change)="$event ? masterToggle() : null"
[checked]="selection.hasValue() && isAllSelected()" [checked]="selection.hasValue() && isAllSelected()"
[indeterminate]="selection.hasValue() && !isAllSelected()"> [indeterminate]="selection.hasValue() && !isAllSelected()"
>
</mat-checkbox> </mat-checkbox>
</th> </th>
<td class="selection" mat-cell *matCellDef="let row"> <td class="selection" mat-cell *matCellDef="let row">
<mat-checkbox color="primary" (click)="$event.stopPropagation()" <mat-checkbox
(change)="$event ? selection.toggle(row) : null" [checked]="selection.isSelected(row)"> color="primary"
(click)="$event.stopPropagation()"
(change)="$event ? selection.toggle(row) : null"
[checked]="selection.isSelected(row)"
>
</mat-checkbox> </mat-checkbox>
</td> </td>
</ng-container> </ng-container>
@@ -26,7 +37,8 @@
<ng-container matColumnDef="name"> <ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef>{{ 'APP.NAME' | translate }}</th> <th mat-header-cell *matHeaderCellDef>{{ 'APP.NAME' | translate }}</th>
<td class="pointer" [routerLink]="['/projects', projectId, 'apps', app.id]" mat-cell *matCellDef="let app"> <td class="pointer" [routerLink]="['/projects', projectId, 'apps', app.id]" mat-cell *matCellDef="let app">
{{app.name}} </td> {{ app.name }}
</td>
</ng-container> </ng-container>
<ng-container matColumnDef="type"> <ng-container matColumnDef="type">
@@ -42,9 +54,14 @@
<ng-container matColumnDef="state"> <ng-container matColumnDef="state">
<th mat-header-cell *matHeaderCellDef>{{ 'USER.DATA.STATE' | translate }}</th> <th mat-header-cell *matHeaderCellDef>{{ 'USER.DATA.STATE' | translate }}</th>
<td class="pointer" mat-cell *matCellDef="let app" [routerLink]="['/projects', projectId, 'apps', app.id]"> <td class="pointer" mat-cell *matCellDef="let app" [routerLink]="['/projects', projectId, 'apps', app.id]">
<span class="state" <span
[ngClass]="{'active': app.state === AppState.APP_STATE_ACTIVE, 'inactive': app.state === AppState.APP_STATE_INACTIVE}"> class="state"
{{('APP.PAGES.DETAIL.STATE.'+app?.state) | translate}} [ngClass]="{
active: app.state === AppState.APP_STATE_ACTIVE,
inactive: app.state === AppState.APP_STATE_INACTIVE
}"
>
{{ 'APP.PAGES.DETAIL.STATE.' + app?.state | translate }}
</span> </span>
</td> </td>
</ng-container> </ng-container>
@@ -52,25 +69,33 @@
<ng-container matColumnDef="creationDate"> <ng-container matColumnDef="creationDate">
<th mat-header-cell *matHeaderCellDef>{{ 'PROJECT.ROLE.CREATIONDATE' | translate }}</th> <th mat-header-cell *matHeaderCellDef>{{ 'PROJECT.ROLE.CREATIONDATE' | translate }}</th>
<td class="pointer" [routerLink]="['/projects', projectId, 'apps', app.id]" mat-cell *matCellDef="let app"> <td class="pointer" [routerLink]="['/projects', projectId, 'apps', app.id]" mat-cell *matCellDef="let app">
<span *ngIf="app?.details?.creationDate">{{app.details.creationDate | timestampToDate | <span *ngIf="app?.details?.creationDate">{{
localizedDate: 'dd. MMM, HH:mm' }}</span> app.details.creationDate | timestampToDate | localizedDate: 'dd. MMM, HH:mm'
}}</span>
</td> </td>
</ng-container> </ng-container>
<ng-container matColumnDef="changeDate"> <ng-container matColumnDef="changeDate">
<th mat-header-cell *matHeaderCellDef>{{ 'PROJECT.ROLE.CHANGEDATE' | translate }}</th> <th mat-header-cell *matHeaderCellDef>{{ 'PROJECT.ROLE.CHANGEDATE' | translate }}</th>
<td class="pointer" [routerLink]="['/projects', projectId, 'apps', app.id]" mat-cell *matCellDef="let app"> <td class="pointer" [routerLink]="['/projects', projectId, 'apps', app.id]" mat-cell *matCellDef="let app">
<span *ngIf="app?.details?.changeDate">{{app.details.changeDate | timestampToDate | <span *ngIf="app?.details?.changeDate">{{
localizedDate: 'dd. MMM, HH:mm' }}</span> app.details.changeDate | timestampToDate | localizedDate: 'dd. MMM, HH:mm'
}}</span>
</td> </td>
</ng-container> </ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr class="highlight" mat-row *matRowDef="let row; columns: displayedColumns;"></tr> <tr class="highlight" mat-row *matRowDef="let row; columns: displayedColumns"></tr>
</table> </table>
</div> </div>
<cnsl-paginator class="paginator" #paginator [timestamp]="dataSource?.viewTimestamp" [length]="dataSource.totalResult" <cnsl-paginator
[pageSize]="25" [pageSizeOptions]="[25, 50, 100, 250]"> class="paginator"
#paginator
[timestamp]="dataSource.viewTimestamp"
[length]="dataSource.totalResult"
[pageSize]="25"
[pageSizeOptions]="[25, 50, 100, 250]"
>
</cnsl-paginator> </cnsl-paginator>
</cnsl-refresh-table> </cnsl-refresh-table>

View File

@@ -19,7 +19,7 @@ export class ApplicationsComponent implements AfterViewInit, OnInit {
@Input() public disabled: boolean = false; @Input() public disabled: boolean = false;
@ViewChild(PaginatorComponent) public paginator!: PaginatorComponent; @ViewChild(PaginatorComponent) public paginator!: PaginatorComponent;
@ViewChild(MatTable) public table!: MatTable<App.AsObject>; @ViewChild(MatTable) public table!: MatTable<App.AsObject>;
public dataSource!: ProjectApplicationsDataSource; public dataSource: ProjectApplicationsDataSource = new ProjectApplicationsDataSource(this.mgmtService);
public selection: SelectionModel<App.AsObject> = new SelectionModel<App.AsObject>(true, []); public selection: SelectionModel<App.AsObject> = new SelectionModel<App.AsObject>(true, []);
public displayedColumns: string[] = ['name', 'type', 'state', 'creationDate', 'changeDate']; public displayedColumns: string[] = ['name', 'type', 'state', 'creationDate', 'changeDate'];
@@ -27,7 +27,6 @@ export class ApplicationsComponent implements AfterViewInit, OnInit {
constructor(private mgmtService: ManagementService) {} constructor(private mgmtService: ManagementService) {}
ngOnInit(): void { ngOnInit(): void {
this.dataSource = new ProjectApplicationsDataSource(this.mgmtService);
this.dataSource.loadApps(this.projectId, 0, 25); this.dataSource.loadApps(this.projectId, 0, 25);
} }

View File

@@ -15,7 +15,7 @@
<button <button
mat-menu-item mat-menu-item
[disabled]="isZitadel || (['project.write$', 'project.write:' + project.id] | hasRole | async) === false" [disabled]="isZitadel || (['project.write$', 'project.write:' + project?.id] | hasRole | async) === false"
*ngIf="project?.state === ProjectState.PROJECT_STATE_ACTIVE" *ngIf="project?.state === ProjectState.PROJECT_STATE_ACTIVE"
(click)="changeState(ProjectState.PROJECT_STATE_INACTIVE)" (click)="changeState(ProjectState.PROJECT_STATE_INACTIVE)"
> >
@@ -24,7 +24,7 @@
<button <button
mat-menu-item mat-menu-item
[disabled]="isZitadel || (['project.write$', 'project.write:' + project.id] | hasRole | async) === false" [disabled]="isZitadel || (['project.write$', 'project.write:' + project?.id] | hasRole | async) === false"
*ngIf="project?.state === ProjectState.PROJECT_STATE_INACTIVE" *ngIf="project?.state === ProjectState.PROJECT_STATE_INACTIVE"
(click)="changeState(ProjectState.PROJECT_STATE_ACTIVE)" (click)="changeState(ProjectState.PROJECT_STATE_ACTIVE)"
> >

View File

@@ -8,9 +8,7 @@ import { BehaviorSubject, from, Observable, of } from 'rxjs';
import { catchError, finalize, map, take } from 'rxjs/operators'; import { catchError, finalize, map, take } from 'rxjs/operators';
import { CreationType, MemberCreateDialogComponent } from 'src/app/modules/add-member-dialog/member-create-dialog.component'; import { CreationType, MemberCreateDialogComponent } from 'src/app/modules/add-member-dialog/member-create-dialog.component';
import { ChangeType } from 'src/app/modules/changes/changes.component'; import { ChangeType } from 'src/app/modules/changes/changes.component';
import { import { ProjectPrivateLabelingDialogComponent } from 'src/app/modules/project-private-labeling-dialog/project-private-labeling-dialog.component';
ProjectPrivateLabelingDialogComponent,
} from 'src/app/modules/project-private-labeling-dialog/project-private-labeling-dialog.component';
import { SidenavSetting } from 'src/app/modules/sidenav/sidenav.component'; import { SidenavSetting } from 'src/app/modules/sidenav/sidenav.component';
import { UserGrantContext } from 'src/app/modules/user-grants/user-grants-datasource'; import { UserGrantContext } from 'src/app/modules/user-grants/user-grants-datasource';
import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component'; import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component';
@@ -39,7 +37,7 @@ const GRANTS: SidenavSetting = { id: 'grants', i18nKey: 'MENU.GRANTS' };
}) })
export class OwnedProjectDetailComponent implements OnInit { export class OwnedProjectDetailComponent implements OnInit {
public projectId: string = ''; public projectId: string = '';
public project!: Project.AsObject; public project?: Project.AsObject;
public pageSizeApps: number = 10; public pageSizeApps: number = 10;
public appsDataSource: MatTableDataSource<App.AsObject> = new MatTableDataSource<App.AsObject>(); public appsDataSource: MatTableDataSource<App.AsObject> = new MatTableDataSource<App.AsObject>();
@@ -95,7 +93,7 @@ export class OwnedProjectDetailComponent implements OnInit {
public openNameDialog(): void { public openNameDialog(): void {
const dialogRef = this.dialog.open(NameDialogComponent, { const dialogRef = this.dialog.open(NameDialogComponent, {
data: { data: {
name: this.project.name, name: this.project?.name,
titleKey: 'PROJECT.NAMEDIALOG.TITLE', titleKey: 'PROJECT.NAMEDIALOG.TITLE',
descKey: 'PROJECT.NAMEDIALOG.DESCRIPTION', descKey: 'PROJECT.NAMEDIALOG.DESCRIPTION',
labelKey: 'PROJECT.NAMEDIALOG.NAME', labelKey: 'PROJECT.NAMEDIALOG.NAME',
@@ -105,7 +103,7 @@ export class OwnedProjectDetailComponent implements OnInit {
dialogRef.afterClosed().subscribe((name) => { dialogRef.afterClosed().subscribe((name) => {
if (name) { if (name) {
this.project.name = name; this.project!.name = name;
this.updateName(); this.updateName();
} }
}); });
@@ -114,14 +112,14 @@ export class OwnedProjectDetailComponent implements OnInit {
public openPrivateLabelingDialog(): void { public openPrivateLabelingDialog(): void {
const dialogRef = this.dialog.open(ProjectPrivateLabelingDialogComponent, { const dialogRef = this.dialog.open(ProjectPrivateLabelingDialogComponent, {
data: { data: {
setting: this.project.privateLabelingSetting, setting: this.project?.privateLabelingSetting,
}, },
width: '400px', width: '400px',
}); });
dialogRef.afterClosed().subscribe((resp: PrivateLabelingSetting) => { dialogRef.afterClosed().subscribe((resp: PrivateLabelingSetting) => {
if (resp !== undefined) { if (resp !== undefined) {
this.project.privateLabelingSetting = resp; this.project!.privateLabelingSetting = resp;
} }
}); });
} }
@@ -143,7 +141,7 @@ export class OwnedProjectDetailComponent implements OnInit {
}), }),
new Breadcrumb({ new Breadcrumb({
type: BreadcrumbType.PROJECT, type: BreadcrumbType.PROJECT,
name: this.project.name, name: this.project?.name,
param: { key: ROUTEPARAM, value: projectId }, param: { key: ROUTEPARAM, value: projectId },
routerLink: ['/projects', projectId], routerLink: ['/projects', projectId],
isZitadel: this.isZitadel, isZitadel: this.isZitadel,
@@ -198,7 +196,7 @@ export class OwnedProjectDetailComponent implements OnInit {
.reactivateProject(this.projectId) .reactivateProject(this.projectId)
.then(() => { .then(() => {
this.toast.showInfo('PROJECT.TOAST.REACTIVATED', true); this.toast.showInfo('PROJECT.TOAST.REACTIVATED', true);
this.project.state = ProjectState.PROJECT_STATE_ACTIVE; this.project!.state = ProjectState.PROJECT_STATE_ACTIVE;
this.refreshChanges$.emit(); this.refreshChanges$.emit();
}) })
.catch((error) => { .catch((error) => {
@@ -222,7 +220,7 @@ export class OwnedProjectDetailComponent implements OnInit {
.deactivateProject(this.projectId) .deactivateProject(this.projectId)
.then(() => { .then(() => {
this.toast.showInfo('PROJECT.TOAST.DEACTIVATED', true); this.toast.showInfo('PROJECT.TOAST.DEACTIVATED', true);
this.project.state = ProjectState.PROJECT_STATE_INACTIVE; this.project!.state = ProjectState.PROJECT_STATE_INACTIVE;
this.refreshChanges$.emit(); this.refreshChanges$.emit();
}) })
.catch((error) => { .catch((error) => {
@@ -262,6 +260,7 @@ export class OwnedProjectDetailComponent implements OnInit {
} }
public saveProject(): void { public saveProject(): void {
if (this.project) {
const req = new UpdateProjectRequest(); const req = new UpdateProjectRequest();
req.setId(this.project.id); req.setId(this.project.id);
req.setName(this.project.name); req.setName(this.project.name);
@@ -280,6 +279,7 @@ export class OwnedProjectDetailComponent implements OnInit {
this.toast.showError(error); this.toast.showError(error);
}); });
} }
}
public navigateBack(): void { public navigateBack(): void {
this._location.back(); this._location.back();
@@ -293,7 +293,7 @@ export class OwnedProjectDetailComponent implements OnInit {
const dialogRef = this.dialog.open(MemberCreateDialogComponent, { const dialogRef = this.dialog.open(MemberCreateDialogComponent, {
data: { data: {
creationType: CreationType.PROJECT_OWNED, creationType: CreationType.PROJECT_OWNED,
projectId: this.project.id, projectId: this.project?.id,
}, },
width: '400px', width: '400px',
}); });
@@ -323,6 +323,8 @@ export class OwnedProjectDetailComponent implements OnInit {
} }
public showDetail(): void { public showDetail(): void {
if (this.project) {
this.router.navigate(['projects', this.project.id, 'members']); this.router.navigate(['projects', this.project.id, 'members']);
} }
} }
}

View File

@@ -19,7 +19,7 @@
</button> </button>
</form> </form>
<span *ngIf="org"> {{ 'PROJECT.GRANT.CREATE.FOR_ORG' | translate }} {{ org?.name }} </span> <span *ngIf="org"> {{ 'PROJECT.GRANT.CREATE.FOR_ORG' | translate }} {{ org.name }} </span>
</ng-container> </ng-container>
<ng-container *ngIf="currentCreateStep === 2"> <ng-container *ngIf="currentCreateStep === 2">

View File

@@ -16,7 +16,7 @@ const ROUTEPARAM = 'projectid';
styleUrls: ['./project-grant-create.component.scss'], styleUrls: ['./project-grant-create.component.scss'],
}) })
export class ProjectGrantCreateComponent implements OnInit, OnDestroy { export class ProjectGrantCreateComponent implements OnInit, OnDestroy {
public org!: Org.AsObject; public org?: Org.AsObject;
public projectId: string = ''; public projectId: string = '';
public grantId: string = ''; public grantId: string = '';
public rolesKeyList: string[] = []; public rolesKeyList: string[] = [];
@@ -77,6 +77,7 @@ export class ProjectGrantCreateComponent implements OnInit, OnDestroy {
} }
public addGrant(): void { public addGrant(): void {
if (this.org) {
this.mgmtService this.mgmtService
.addProjectGrant(this.org.id, this.projectId, this.rolesKeyList) .addProjectGrant(this.org.id, this.projectId, this.rolesKeyList)
.then(() => { .then(() => {
@@ -86,6 +87,7 @@ export class ProjectGrantCreateComponent implements OnInit, OnDestroy {
this.toast.showError(error); this.toast.showError(error);
}); });
} }
}
public selectRoles(roles: Role.AsObject[]): void { public selectRoles(roles: Role.AsObject[]): void {
this.rolesKeyList = roles.map((role) => role.key); this.rolesKeyList = roles.map((role) => role.key);

View File

@@ -2,12 +2,12 @@
<div class="project-grants-wrapper"> <div class="project-grants-wrapper">
<div class="project-grants"> <div class="project-grants">
<cnsl-refresh-table <cnsl-refresh-table
[loading]="dataSource?.loading$ | async" [loading]="dataSource.loading$ | async"
*ngIf="projectId" *ngIf="projectId"
(refreshed)="loadGrantsPage()" (refreshed)="loadGrantsPage()"
[dataSize]="dataSource.totalResult" [dataSize]="dataSource.totalResult"
[selection]="selection" [selection]="selection"
[timestamp]="dataSource?.viewTimestamp" [timestamp]="dataSource.viewTimestamp"
(refreshed)="getRoleOptions(projectId)" (refreshed)="getRoleOptions(projectId)"
> >
<div actions> <div actions>
@@ -149,7 +149,7 @@
class="paginator" class="paginator"
#paginator #paginator
[pageSize]="50" [pageSize]="50"
[timestamp]="dataSource?.viewTimestamp" [timestamp]="dataSource.viewTimestamp"
[pageSizeOptions]="[25, 50, 100, 250]" [pageSizeOptions]="[25, 50, 100, 250]"
[length]="dataSource.totalResult" [length]="dataSource.totalResult"
(page)="loadGrantsPage($event.pageIndex, $event.pageSize)" (page)="loadGrantsPage($event.pageIndex, $event.pageSize)"

View File

@@ -32,12 +32,12 @@ export class ProjectGrantsComponent implements OnInit, AfterViewInit {
@Input() public projectId: string = ''; @Input() public projectId: string = '';
@ViewChild(PaginatorComponent) public paginator!: PaginatorComponent; @ViewChild(PaginatorComponent) public paginator!: PaginatorComponent;
@ViewChild(MatTable) public table!: MatTable<GrantedProject.AsObject>; @ViewChild(MatTable) public table!: MatTable<GrantedProject.AsObject>;
public dataSource!: ProjectGrantsDataSource; public dataSource: ProjectGrantsDataSource = new ProjectGrantsDataSource(this.mgmtService, this.toast);
public selection: SelectionModel<GrantedProject.AsObject> = new SelectionModel<GrantedProject.AsObject>(true, []); public selection: SelectionModel<GrantedProject.AsObject> = new SelectionModel<GrantedProject.AsObject>(true, []);
public memberRoleOptions: Role.AsObject[] = []; public memberRoleOptions: Role.AsObject[] = [];
public displayedColumns: string[] = ['grantedOrgName', 'state', 'creationDate', 'changeDate', 'roleNamesList', 'actions']; public displayedColumns: string[] = ['grantedOrgName', 'state', 'creationDate', 'changeDate', 'roleNamesList', 'actions'];
ProjectGrantState: any = ProjectGrantState; public ProjectGrantState: any = ProjectGrantState;
constructor( constructor(
private mgmtService: ManagementService, private mgmtService: ManagementService,
@@ -51,7 +51,6 @@ export class ProjectGrantsComponent implements OnInit, AfterViewInit {
} }
public ngOnInit(): void { public ngOnInit(): void {
this.dataSource = new ProjectGrantsDataSource(this.mgmtService, this.toast);
this.dataSource.loadGrants(this.projectId, 0, 25, 'asc'); this.dataSource.loadGrants(this.projectId, 0, 25, 'asc');
this.getRoleOptions(this.projectId); this.getRoleOptions(this.projectId);
} }

View File

@@ -40,7 +40,9 @@
<cnsl-project-roles-table <cnsl-project-roles-table
[displayedColumns]="['select', 'key', 'displayname', 'group', 'creationDate', 'changeDate']" [displayedColumns]="['select', 'key', 'displayname', 'group', 'creationDate', 'changeDate']"
(changedSelection)="selectRoles($event)" (changedSelection)="selectRoles($event)"
[projectId]="project?.id ? project.id : grantedProject?.projectId ? grantedProject.projectId : ''" [projectId]="
project && project.id ? project.id : grantedProject && grantedProject.projectId ? grantedProject.projectId : ''
"
[grantId]="$any(grantedProject)?.grantId ? $any(grantedProject)?.grantId : ''" [grantId]="$any(grantedProject)?.grantId ? $any(grantedProject)?.grantId : ''"
> >
</cnsl-project-roles-table> </cnsl-project-roles-table>

View File

@@ -21,11 +21,11 @@ import { ToastService } from 'src/app/services/toast.service';
export class UserGrantCreateComponent implements OnDestroy { export class UserGrantCreateComponent implements OnDestroy {
public context!: UserGrantContext; public context!: UserGrantContext;
public org!: Org.AsObject; public org?: Org.AsObject;
public userIds: string[] = []; public userIds: string[] = [];
public project!: Project.AsObject; public project?: Project.AsObject;
public grantedProject!: GrantedProject.AsObject; public grantedProject?: GrantedProject.AsObject;
public rolesList: string[] = []; public rolesList: string[] = [];
@@ -34,7 +34,7 @@ export class UserGrantCreateComponent implements OnDestroy {
public UserGrantContext: any = UserGrantContext; public UserGrantContext: any = UserGrantContext;
public user!: User.AsObject; public user?: User.AsObject;
public UserTarget: any = UserTarget; public UserTarget: any = UserTarget;
public editState: boolean = false; public editState: boolean = false;
@@ -114,7 +114,7 @@ export class UserGrantCreateComponent implements OnDestroy {
public addGrant(): void { public addGrant(): void {
switch (this.context) { switch (this.context) {
case UserGrantContext.OWNED_PROJECT: case UserGrantContext.OWNED_PROJECT:
const prom = this.userIds.map((id) => this.userService.addUserGrant(id, this.rolesList, this.project.id)); const prom = this.userIds.map((id) => this.userService.addUserGrant(id, this.rolesList, this.project?.id));
Promise.all(prom) Promise.all(prom)
.then(() => { .then(() => {
this.toast.showInfo('GRANTS.TOAST.UPDATED', true); this.toast.showInfo('GRANTS.TOAST.UPDATED', true);
@@ -127,7 +127,7 @@ export class UserGrantCreateComponent implements OnDestroy {
break; break;
case UserGrantContext.GRANTED_PROJECT: case UserGrantContext.GRANTED_PROJECT:
const promp = this.userIds.map((id) => const promp = this.userIds.map((id) =>
this.userService.addUserGrant(id, this.rolesList, this.grantedProject.projectId, this.grantedProject.grantId), this.userService.addUserGrant(id, this.rolesList, this.grantedProject?.projectId, this.grantedProject?.grantId),
); );
Promise.all(promp) Promise.all(promp)
.then(() => { .then(() => {

View File

@@ -1,20 +1,38 @@
<cnsl-card title="{{'USER.PASSWORDLESS.TITLE' | translate}}" <cnsl-card
description="{{'USER.PASSWORDLESS.DESCRIPTION' | translate}}"> title="{{ 'USER.PASSWORDLESS.TITLE' | translate }}"
<button card-actions mat-icon-button (click)="getPasswordless()" class="icon-button" description="{{ 'USER.PASSWORDLESS.DESCRIPTION' | translate }}"
matTooltip="{{'ACTIONS.REFRESH' | translate}}"> >
<button
card-actions
mat-icon-button
(click)="getPasswordless()"
class="icon-button"
matTooltip="{{ 'ACTIONS.REFRESH' | translate }}"
>
<mat-icon class="icon">refresh</mat-icon> <mat-icon class="icon">refresh</mat-icon>
</button> </button>
<cnsl-refresh-table [hideRefresh]="true" [loading]="loading$ | async" (refreshed)="getPasswordless()" <cnsl-refresh-table
[dataSize]="dataSource?.data?.length ?? 0"> [hideRefresh]="true"
<button actions class="cnsl-action-button button" (click)="addPasswordless()" mat-raised-button color="primary" [loading]="loading$ | async"
matTooltip="{{'ACTIONS.NEW' | translate}}"> (refreshed)="getPasswordless()"
[dataSize]="dataSource.data.length"
>
<button
actions
class="cnsl-action-button button"
(click)="addPasswordless()"
mat-raised-button
color="primary"
matTooltip="{{ 'ACTIONS.NEW' | translate }}"
>
<i class="icon las la-fingerprint"></i> <i class="icon las la-fingerprint"></i>
{{ 'USER.PASSWORDLESS.U2F' | translate }} {{ 'USER.PASSWORDLESS.U2F' | translate }}
</button> </button>
<table class="table" mat-table [dataSource]="dataSource"> <table class="table" mat-table [dataSource]="dataSource">
<ng-container matColumnDef="name"> <ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef>{{ 'USER.PASSWORDLESS.NAME' | translate }}</th> <th mat-header-cell *matHeaderCellDef>{{ 'USER.PASSWORDLESS.NAME' | translate }}</th>
<td mat-cell *matCellDef="let mfa"><span *ngIf="mfa?.name" class="centered"> <td mat-cell *matCellDef="let mfa">
<span *ngIf="mfa?.name" class="centered">
{{ mfa?.name }} {{ mfa?.name }}
</span> </span>
</td> </td>
@@ -22,10 +40,16 @@
<ng-container matColumnDef="state"> <ng-container matColumnDef="state">
<th mat-header-cell *matHeaderCellDef>{{ 'USER.PASSWORDLESS.TABLESTATE' | translate }}</th> <th mat-header-cell *matHeaderCellDef>{{ 'USER.PASSWORDLESS.TABLESTATE' | translate }}</th>
<td mat-cell *matCellDef="let mfa"><span class="centered"> <td mat-cell *matCellDef="let mfa">
<span class="state" <span class="centered">
[ngClass]="{'active': mfa.state === AuthFactorState.AUTH_FACTOR_STATE_READY, 'inactive': mfa.state === AuthFactorState.AUTH_FACTOR_STATE_NOT_READY }">{{'USER.PASSWORDLESS.STATE.'+ <span
mfa.state | translate}}</span> class="state"
[ngClass]="{
active: mfa.state === AuthFactorState.AUTH_FACTOR_STATE_READY,
inactive: mfa.state === AuthFactorState.AUTH_FACTOR_STATE_NOT_READY
}"
>{{ 'USER.PASSWORDLESS.STATE.' + mfa.state | translate }}</span
>
</span> </span>
</td> </td>
</ng-container> </ng-container>
@@ -34,8 +58,13 @@
<th mat-header-cell *matHeaderCellDef></th> <th mat-header-cell *matHeaderCellDef></th>
<td mat-cell *matCellDef="let mfa"> <td mat-cell *matCellDef="let mfa">
<cnsl-table-actions> <cnsl-table-actions>
<button actions matTooltip="{{'ACTIONS.REMOVE' | translate}}" color="warn" mat-icon-button <button
(click)="deletePasswordless(mfa.id)"> actions
matTooltip="{{ 'ACTIONS.REMOVE' | translate }}"
color="warn"
mat-icon-button
(click)="deletePasswordless(mfa.id)"
>
<i class="las la-trash"></i> <i class="las la-trash"></i>
</button> </button>
</cnsl-table-actions> </cnsl-table-actions>
@@ -43,7 +72,7 @@
</ng-container> </ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr class="highlight" mat-row *matRowDef="let row; columns: displayedColumns;"></tr> <tr class="highlight" mat-row *matRowDef="let row; columns: displayedColumns"></tr>
</table> </table>
<div *ngIf="(loading$ | async) === false && !dataSource?.data?.length" class="no-content-row"> <div *ngIf="(loading$ | async) === false && !dataSource?.data?.length" class="no-content-row">
<i class="las la-exclamation"></i> <i class="las la-exclamation"></i>

View File

@@ -34,7 +34,7 @@ export class AuthPasswordlessComponent implements OnInit, OnDestroy {
@ViewChild(MatTable) public table!: MatTable<WebAuthNToken.AsObject>; @ViewChild(MatTable) public table!: MatTable<WebAuthNToken.AsObject>;
@ViewChild(MatSort) public sort!: MatSort; @ViewChild(MatSort) public sort!: MatSort;
public dataSource!: MatTableDataSource<WebAuthNToken.AsObject>; public dataSource: MatTableDataSource<WebAuthNToken.AsObject> = new MatTableDataSource<WebAuthNToken.AsObject>([]);
public AuthFactorState: any = AuthFactorState; public AuthFactorState: any = AuthFactorState;
public error: string = ''; public error: string = '';

View File

@@ -1,5 +1,5 @@
<cnsl-top-view <cnsl-top-view
title="{{ user?.human ? user.human?.profile?.displayName : user?.machine?.name }}" title="{{ user && user.human ? user.human.profile?.displayName : user?.machine?.name }}"
sub="{{ user?.preferredLoginName }}" sub="{{ user?.preferredLoginName }}"
[isActive]="user?.state === UserState.USER_STATE_ACTIVE" [isActive]="user?.state === UserState.USER_STATE_ACTIVE"
[isInactive]="user?.state === UserState.USER_STATE_INACTIVE" [isInactive]="user?.state === UserState.USER_STATE_INACTIVE"
@@ -95,7 +95,7 @@
<ng-container *ngIf="currentSetting === 'grants'"> <ng-container *ngIf="currentSetting === 'grants'">
<cnsl-card <cnsl-card
*ngIf="user?.id" *ngIf="user && user.id"
title="{{ 'GRANTS.USER.TITLE' | translate }}" title="{{ 'GRANTS.USER.TITLE' | translate }}"
description="{{ 'GRANTS.USER.DESCRIPTION' | translate }}" description="{{ 'GRANTS.USER.DESCRIPTION' | translate }}"
> >
@@ -130,7 +130,7 @@
</ng-container> </ng-container>
<ng-container *ngIf="currentSetting === 'metadata'"> <ng-container *ngIf="currentSetting === 'metadata'">
<cnsl-metadata *ngIf="user?.id" [userId]="user.id"></cnsl-metadata> <cnsl-metadata *ngIf="user && user.id" [userId]="user.id"></cnsl-metadata>
</ng-container> </ng-container>
</cnsl-sidenav> </cnsl-sidenav>

View File

@@ -23,7 +23,7 @@ import { EditDialogComponent, EditDialogType } from './edit-dialog/edit-dialog.c
styleUrls: ['./auth-user-detail.component.scss'], styleUrls: ['./auth-user-detail.component.scss'],
}) })
export class AuthUserDetailComponent implements OnDestroy { export class AuthUserDetailComponent implements OnDestroy {
public user!: User.AsObject; public user?: User.AsObject;
public genders: Gender[] = [Gender.GENDER_MALE, Gender.GENDER_FEMALE, Gender.GENDER_DIVERSE]; public genders: Gender[] = [Gender.GENDER_MALE, Gender.GENDER_FEMALE, Gender.GENDER_DIVERSE];
public languages: string[] = ['de', 'en', 'it']; public languages: string[] = ['de', 'en', 'it'];
@@ -132,13 +132,13 @@ export class AuthUserDetailComponent implements OnDestroy {
labelKey: 'ACTIONS.NEWVALUE', labelKey: 'ACTIONS.NEWVALUE',
titleKey: 'USER.PROFILE.CHANGEUSERNAME_TITLE', titleKey: 'USER.PROFILE.CHANGEUSERNAME_TITLE',
descriptionKey: 'USER.PROFILE.CHANGEUSERNAME_DESC', descriptionKey: 'USER.PROFILE.CHANGEUSERNAME_DESC',
value: this.user.userName, value: this.user?.userName,
}, },
width: '400px', width: '400px',
}); });
dialogRef.afterClosed().subscribe((resp: { value: string }) => { dialogRef.afterClosed().subscribe((resp: { value: string }) => {
if (resp && resp.value && resp.value !== this.user.userName) { if (resp && resp.value && resp.value !== this.user?.userName) {
this.userService this.userService
.updateMyUserName(resp.value) .updateMyUserName(resp.value)
.then(() => { .then(() => {
@@ -153,7 +153,7 @@ export class AuthUserDetailComponent implements OnDestroy {
} }
public saveProfile(profileData: Profile.AsObject): void { public saveProfile(profileData: Profile.AsObject): void {
if (this.user.human) { if (this.user?.human) {
this.user.human.profile = profileData; this.user.human.profile = profileData;
this.userService this.userService
@@ -180,7 +180,7 @@ export class AuthUserDetailComponent implements OnDestroy {
.setMyEmail(email) .setMyEmail(email)
.then(() => { .then(() => {
this.toast.showInfo('USER.TOAST.EMAILSAVED', true); this.toast.showInfo('USER.TOAST.EMAILSAVED', true);
if (this.user.human) { if (this.user?.human) {
const mailToSet = new Email(); const mailToSet = new Email();
mailToSet.setEmail(email); mailToSet.setEmail(email);
this.user.human.email = mailToSet.toObject(); this.user.human.email = mailToSet.toObject();
@@ -237,7 +237,7 @@ export class AuthUserDetailComponent implements OnDestroy {
.removeMyPhone() .removeMyPhone()
.then(() => { .then(() => {
this.toast.showInfo('USER.TOAST.PHONEREMOVED', true); this.toast.showInfo('USER.TOAST.PHONEREMOVED', true);
if (this.user.human?.phone) { if (this.user?.human?.phone) {
const phone = new Phone(); const phone = new Phone();
this.user.human.phone = phone.toObject(); this.user.human.phone = phone.toObject();
this.refreshUser(); this.refreshUser();
@@ -249,12 +249,12 @@ export class AuthUserDetailComponent implements OnDestroy {
} }
public savePhone(phone: string): void { public savePhone(phone: string): void {
if (this.user.human) { if (this.user?.human) {
this.userService this.userService
.setMyPhone(phone) .setMyPhone(phone)
.then(() => { .then(() => {
this.toast.showInfo('USER.TOAST.PHONESAVED', true); this.toast.showInfo('USER.TOAST.PHONESAVED', true);
if (this.user.human) { if (this.user?.human) {
const phoneToSet = new Phone(); const phoneToSet = new Phone();
phoneToSet.setPhone(phone); phoneToSet.setPhone(phone);
this.user.human.phone = phoneToSet.toObject(); this.user.human.phone = phoneToSet.toObject();
@@ -277,7 +277,7 @@ export class AuthUserDetailComponent implements OnDestroy {
labelKey: 'USER.LOGINMETHODS.PHONE.EDITVALUE', labelKey: 'USER.LOGINMETHODS.PHONE.EDITVALUE',
titleKey: 'USER.LOGINMETHODS.PHONE.EDITTITLE', titleKey: 'USER.LOGINMETHODS.PHONE.EDITTITLE',
descriptionKey: 'USER.LOGINMETHODS.PHONE.EDITDESC', descriptionKey: 'USER.LOGINMETHODS.PHONE.EDITDESC',
value: this.user.human?.phone?.phone, value: this.user?.human?.phone?.phone,
type: type, type: type,
}, },
width: '400px', width: '400px',
@@ -297,7 +297,7 @@ export class AuthUserDetailComponent implements OnDestroy {
labelKey: 'ACTIONS.NEWVALUE', labelKey: 'ACTIONS.NEWVALUE',
titleKey: 'USER.LOGINMETHODS.EMAIL.EDITTITLE', titleKey: 'USER.LOGINMETHODS.EMAIL.EDITTITLE',
descriptionKey: 'USER.LOGINMETHODS.EMAIL.EDITDESC', descriptionKey: 'USER.LOGINMETHODS.EMAIL.EDITDESC',
value: this.user.human?.email?.email, value: this.user?.human?.email?.email,
type: type, type: type,
}, },
width: '400px', width: '400px',

View File

@@ -1,13 +1,28 @@
<cnsl-card title="{{ 'USER.MFA.TITLE' | translate }}" description="{{ 'USER.MFA.DESCRIPTION' | translate }}"> <cnsl-card title="{{ 'USER.MFA.TITLE' | translate }}" description="{{ 'USER.MFA.DESCRIPTION' | translate }}">
<button card-actions mat-icon-button (click)="getMFAs()" class="icon-button" <button
matTooltip="{{'ACTIONS.REFRESH' | translate}}"> card-actions
mat-icon-button
(click)="getMFAs()"
class="icon-button"
matTooltip="{{ 'ACTIONS.REFRESH' | translate }}"
>
<mat-icon class="icon">refresh</mat-icon> <mat-icon class="icon">refresh</mat-icon>
</button> </button>
<cnsl-refresh-table [hideRefresh]="true" [loading]="loading$ | async" (refreshed)="getMFAs()" <cnsl-refresh-table
[dataSize]="dataSource?.data?.length ?? 0"> [hideRefresh]="true"
<button actions class="button cnsl-action-button" (click)="addAuthFactor()" mat-raised-button color="primary" [loading]="loading$ | async"
matTooltip="{{'ACTIONS.NEW' | translate}}"> (refreshed)="getMFAs()"
[dataSize]="dataSource.data.length"
>
<button
actions
class="button cnsl-action-button"
(click)="addAuthFactor()"
mat-raised-button
color="primary"
matTooltip="{{ 'ACTIONS.NEW' | translate }}"
>
<mat-icon class="icon">add</mat-icon> <mat-icon class="icon">add</mat-icon>
<span>{{ 'USER.MFA.ADD' | translate }}</span> <span>{{ 'USER.MFA.ADD' | translate }}</span>
</button> </button>
@@ -32,10 +47,16 @@
<ng-container matColumnDef="state"> <ng-container matColumnDef="state">
<th mat-header-cell *matHeaderCellDef>{{ 'USER.MFA.TABLESTATE' | translate }}</th> <th mat-header-cell *matHeaderCellDef>{{ 'USER.MFA.TABLESTATE' | translate }}</th>
<td mat-cell *matCellDef="let mfa"><span class="centered"> <td mat-cell *matCellDef="let mfa">
<span class="state" <span class="centered">
[ngClass]="{'active': mfa.state === AuthFactorState.AUTH_FACTOR_STATE_READY, 'inactive': mfa.state === AuthFactorState.AUTH_FACTOR_STATE_NOT_READY}">{{'USER.MFA.STATE.'+ <span
mfa.state | translate}}</span> class="state"
[ngClass]="{
active: mfa.state === AuthFactorState.AUTH_FACTOR_STATE_READY,
inactive: mfa.state === AuthFactorState.AUTH_FACTOR_STATE_NOT_READY
}"
>{{ 'USER.MFA.STATE.' + mfa.state | translate }}</span
>
</span> </span>
</td> </td>
</ng-container> </ng-container>
@@ -44,8 +65,13 @@
<th mat-header-cell *matHeaderCellDef></th> <th mat-header-cell *matHeaderCellDef></th>
<td mat-cell *matCellDef="let mfa"> <td mat-cell *matCellDef="let mfa">
<cnsl-table-actions> <cnsl-table-actions>
<button actions matTooltip="{{'ACTIONS.REMOVE' | translate}}" color="warn" mat-icon-button <button
(click)="deleteMFA(mfa)"> actions
matTooltip="{{ 'ACTIONS.REMOVE' | translate }}"
color="warn"
mat-icon-button
(click)="deleteMFA(mfa)"
>
<i class="las la-trash"></i> <i class="las la-trash"></i>
</button> </button>
</cnsl-table-actions> </cnsl-table-actions>
@@ -53,7 +79,7 @@
</ng-container> </ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr class="highlight" mat-row *matRowDef="let row; columns: displayedColumns;"></tr> <tr class="highlight" mat-row *matRowDef="let row; columns: displayedColumns"></tr>
</table> </table>
<div *ngIf="(loading$ | async) === false && !dataSource?.data?.length" class="no-content-row"> <div *ngIf="(loading$ | async) === false && !dataSource?.data?.length" class="no-content-row">

View File

@@ -32,7 +32,7 @@ export class AuthUserMfaComponent implements OnInit, OnDestroy {
@ViewChild(MatTable) public table!: MatTable<AuthFactor.AsObject>; @ViewChild(MatTable) public table!: MatTable<AuthFactor.AsObject>;
@ViewChild(MatSort) public sort!: MatSort; @ViewChild(MatSort) public sort!: MatSort;
public dataSource!: MatTableDataSource<AuthFactor.AsObject>; public dataSource: MatTableDataSource<AuthFactor.AsObject> = new MatTableDataSource<AuthFactor.AsObject>([]);
public AuthFactorState: any = AuthFactorState; public AuthFactorState: any = AuthFactorState;

View File

@@ -1,4 +1,4 @@
<div class="contact-method-col"> <div class="contact-method-col" *ngIf="human">
<div class="contact-method-row"> <div class="contact-method-row">
<div class="left"> <div class="left">
<span class="label cnsl-secondary-text">{{ 'USER.PROFILE.PASSWORD' | translate }}</span> <span class="label cnsl-secondary-text">{{ 'USER.PROFILE.PASSWORD' | translate }}</span>
@@ -8,8 +8,12 @@
</div> </div>
<div class="right"> <div class="right">
<a matTooltip="{{'USER.PASSWORD.SET' | translate}}" [disabled]="!canWrite" [routerLink]="['password']" <a
mat-icon-button> matTooltip="{{ 'USER.PASSWORD.SET' | translate }}"
[disabled]="!canWrite"
[routerLink]="['password']"
mat-icon-button
>
<i class="las la-pen"></i> <i class="las la-pen"></i>
</a> </a>
</div> </div>
@@ -18,16 +22,19 @@
<div class="contact-method-row"> <div class="contact-method-row">
<div class="left"> <div class="left">
<span class="label cnsl-secondary-text">{{ 'USER.EMAIL' | translate }}</span> <span class="label cnsl-secondary-text">{{ 'USER.EMAIL' | translate }}</span>
<span class="name">{{human?.email?.email}}</span> <span class="name">{{ human.email?.email }}</span>
<span *ngIf="human?.email?.isEmailVerified" class="contact-state verified">{{'USER.EMAILVERIFIED' | <span *ngIf="human.email?.isEmailVerified" class="contact-state verified">{{ 'USER.EMAILVERIFIED' | translate }}</span>
translate}}</span> <div *ngIf="!human.email?.isEmailVerified" class="block">
<div *ngIf="!human?.email?.isEmailVerified" class="block">
<span class="contact-state notverified">{{ 'USER.NOTVERIFIED' | translate }}</span> <span class="contact-state notverified">{{ 'USER.NOTVERIFIED' | translate }}</span>
<ng-container *ngIf="human?.email"> <ng-container *ngIf="human.email">
<a *ngIf="canWrite" class="verify cnsl-secondary-text" <a
*ngIf="canWrite"
class="verify cnsl-secondary-text"
matTooltip="{{ 'USER.LOGINMETHODS.EMAIL.RESEND' | translate }}" matTooltip="{{ 'USER.LOGINMETHODS.EMAIL.RESEND' | translate }}"
(click)="emitEmailVerification()">{{'USER.LOGINMETHODS.RESENDCODE' | translate}}</a> (click)="emitEmailVerification()"
>{{ 'USER.LOGINMETHODS.RESENDCODE' | translate }}</a
>
</ng-container> </ng-container>
</div> </div>
@@ -35,9 +42,12 @@
</div> </div>
<div class="right"> <div class="right">
<button matTooltip="{{'ACTIONS.EDIT' | translate}}" <button
matTooltip="{{ 'ACTIONS.EDIT' | translate }}"
[disabled]="!canWrite || state === UserState.USER_STATE_INITIAL" [disabled]="!canWrite || state === UserState.USER_STATE_INITIAL"
(click)="openEditDialog(EditDialogType.EMAIL)" mat-icon-button> (click)="openEditDialog(EditDialogType.EMAIL)"
mat-icon-button
>
<i class="las la-pen"></i> <i class="las la-pen"></i>
</button> </button>
</div> </div>
@@ -46,19 +56,26 @@
<div class="contact-method-row"> <div class="contact-method-row">
<div class="left"> <div class="left">
<span class="label cnsl-secondary-text">{{ 'USER.PHONE' | translate }}</span> <span class="label cnsl-secondary-text">{{ 'USER.PHONE' | translate }}</span>
<span class="name">{{human?.phone?.phone ? human.phone?.phone : ('USER.PHONEEMPTY' | translate)}}</span> <span class="name">{{ human.phone?.phone ? human.phone?.phone : ('USER.PHONEEMPTY' | translate) }}</span>
<span *ngIf="human?.phone?.isPhoneVerified" class="contact-state verified">{{'USER.PHONEVERIFIED' | <span *ngIf="human.phone?.isPhoneVerified" class="contact-state verified">{{ 'USER.PHONEVERIFIED' | translate }}</span>
translate}}</span> <div *ngIf="human.phone?.phone && !human.phone?.isPhoneVerified" class="block">
<div *ngIf="human.phone?.phone && !human?.phone?.isPhoneVerified" class="block">
<span class="contact-state notverified">{{ 'USER.NOTVERIFIED' | translate }}</span> <span class="contact-state notverified">{{ 'USER.NOTVERIFIED' | translate }}</span>
<ng-container *ngIf="human?.phone?.phone"> <ng-container *ngIf="human.phone?.phone">
<a *ngIf="!disablePhoneCode && canWrite" class="verify cnsl-secondary-text" <a
*ngIf="!disablePhoneCode && canWrite"
class="verify cnsl-secondary-text"
matTooltip="{{ 'USER.LOGINMETHODS.ENTERCODE_DESC' | translate }}" matTooltip="{{ 'USER.LOGINMETHODS.ENTERCODE_DESC' | translate }}"
(click)="enterCode()">{{'USER.LOGINMETHODS.ENTERCODE' | translate}}</a> (click)="enterCode()"
<a *ngIf="canWrite" class="verify cnsl-secondary-text" >{{ 'USER.LOGINMETHODS.ENTERCODE' | translate }}</a
>
<a
*ngIf="canWrite"
class="verify cnsl-secondary-text"
matTooltip="{{ 'USER.LOGINMETHODS.PHONE.RESEND' | translate }}" matTooltip="{{ 'USER.LOGINMETHODS.PHONE.RESEND' | translate }}"
(click)="emitPhoneVerification()">{{'USER.LOGINMETHODS.RESENDCODE' | translate}}</a> (click)="emitPhoneVerification()"
>{{ 'USER.LOGINMETHODS.RESENDCODE' | translate }}</a
>
</ng-container> </ng-container>
</div> </div>
@@ -66,12 +83,21 @@
</div> </div>
<div class="right"> <div class="right">
<button matTooltip="{{'ACTIONS.DELETE' | translate}}" *ngIf="human && human.phone?.phone" color="warn" <button
(click)="emitDeletePhone()" mat-icon-button> matTooltip="{{ 'ACTIONS.DELETE' | translate }}"
*ngIf="human && human.phone?.phone"
color="warn"
(click)="emitDeletePhone()"
mat-icon-button
>
<i class="las la-trash"></i> <i class="las la-trash"></i>
</button> </button>
<button matTooltip="{{'ACTIONS.EDIT' | translate}}" [disabled]="!canWrite" <button
(click)="openEditDialog(EditDialogType.PHONE)" mat-icon-button> matTooltip="{{ 'ACTIONS.EDIT' | translate }}"
[disabled]="!canWrite"
(click)="openEditDialog(EditDialogType.PHONE)"
mat-icon-button
>
<i class="las la-pen"></i> <i class="las la-pen"></i>
</button> </button>
</div> </div>

View File

@@ -14,7 +14,7 @@ import { EditDialogType } from '../auth-user-detail/edit-dialog/edit-dialog.comp
export class ContactComponent { export class ContactComponent {
@Input() disablePhoneCode: boolean = false; @Input() disablePhoneCode: boolean = false;
@Input() canWrite: boolean | null = false; @Input() canWrite: boolean | null = false;
@Input() human!: Human.AsObject; @Input() human?: Human.AsObject;
@Input() state!: UserState; @Input() state!: UserState;
@Output() editType: EventEmitter<EditDialogType> = new EventEmitter<EditDialogType>(); @Output() editType: EventEmitter<EditDialogType> = new EventEmitter<EditDialogType>();
@Output() resendEmailVerification: EventEmitter<void> = new EventEmitter<void>(); @Output() resendEmailVerification: EventEmitter<void> = new EventEmitter<void>();

View File

@@ -14,7 +14,7 @@
*ngIf="user && user.profile?.displayName && user.profile?.firstName && user.profile?.lastName" *ngIf="user && user.profile?.displayName && user.profile?.firstName && user.profile?.lastName"
class="avatar" class="avatar"
[name]="user.profile?.displayName ?? ''" [name]="user.profile?.displayName ?? ''"
[avatarUrl]="user?.profile?.avatarUrl || ''" [avatarUrl]="user.profile?.avatarUrl || ''"
[forColor]="preferredLoginName" [forColor]="preferredLoginName"
[size]="80" [size]="80"
> >