diff --git a/console/src/app/modules/project-roles-table/project-roles-table.component.ts b/console/src/app/modules/project-roles-table/project-roles-table.component.ts index 907a11816..473068367 100644 --- a/console/src/app/modules/project-roles-table/project-roles-table.component.ts +++ b/console/src/app/modules/project-roles-table/project-roles-table.component.ts @@ -1,5 +1,5 @@ import { SelectionModel } from '@angular/cdk/collections'; -import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core'; +import { Component, DestroyRef, EventEmitter, inject, Input, OnInit, Output, ViewChild } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; import { MatTable } from '@angular/material/table'; import { Router } from '@angular/router'; @@ -11,6 +11,7 @@ import { PaginatorComponent } from '../paginator/paginator.component'; import { ProjectRoleDetailDialogComponent } from '../project-role-detail-dialog/project-role-detail-dialog.component'; import { WarnDialogComponent } from '../warn-dialog/warn-dialog.component'; import { ProjectRolesDataSource } from './project-roles-table-datasource'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @Component({ selector: 'cnsl-project-roles-table', @@ -33,6 +34,8 @@ export class ProjectRolesTableComponent implements OnInit { @Output() public changedSelection: EventEmitter> = new EventEmitter(); @Input() public displayedColumns: string[] = ['key', 'displayname', 'group', 'creationDate', 'changeDate', 'actions']; + private readonly destroyRef = inject(DestroyRef); + constructor( private mgmtService: ManagementService, private toast: ToastService, @@ -48,7 +51,7 @@ export class ProjectRolesTableComponent implements OnInit { this.loadRolesPage(); this.selection.select(...this.selectedKeys); - this.selection.changed.subscribe(() => { + this.selection.changed.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => { this.changedSelection.emit(this.selection.selected); }); } diff --git a/console/src/app/modules/user-grant-role-dialog/user-grant-role-dialog.component.html b/console/src/app/modules/user-grant-role-dialog/user-grant-role-dialog.component.html index 81f440d46..6a83bf039 100644 --- a/console/src/app/modules/user-grant-role-dialog/user-grant-role-dialog.component.html +++ b/console/src/app/modules/user-grant-role-dialog/user-grant-role-dialog.component.html @@ -1,17 +1,17 @@ {{ data.i18nTitle | translate }}
- - + @if (data.projectId) { + + }
diff --git a/console/src/app/modules/user-grant-role-dialog/user-grant-role-dialog.component.ts b/console/src/app/modules/user-grant-role-dialog/user-grant-role-dialog.component.ts index 7f8f6ede7..ba839ac63 100644 --- a/console/src/app/modules/user-grant-role-dialog/user-grant-role-dialog.component.ts +++ b/console/src/app/modules/user-grant-role-dialog/user-grant-role-dialog.component.ts @@ -1,6 +1,15 @@ -import { Component, Inject } from '@angular/core'; +import { Component, inject, Inject, signal } from '@angular/core'; import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; +export type UserGrantRoleDialogData = { + projectId: string; + grantId?: string; + selectedRoleKeysList: string[]; + i18nTitle: string; +}; + +export type UserGrantRoleDialogResult = { roles: string[] }; + @Component({ selector: 'cnsl-user-grant-role-dialog', templateUrl: './user-grant-role-dialog.component.html', @@ -8,30 +17,19 @@ import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; standalone: false, }) export class UserGrantRoleDialogComponent { - public projectId: string = ''; - public grantId: string = ''; - public selectedRoleKeysList: string[] = []; - - public selectedRoleKeys: string[] = []; - - constructor( - public dialogRef: MatDialogRef, - @Inject(MAT_DIALOG_DATA) public data: any, - ) { - this.projectId = data.projectId; - this.grantId = data.grantId; - this.selectedRoleKeysList = data.selectedRoleKeysList; - } + protected readonly data: UserGrantRoleDialogData = inject(MAT_DIALOG_DATA); + private readonly dialogRef: MatDialogRef = inject(MatDialogRef); + private readonly selectedRoleKeys = signal([...this.data.selectedRoleKeysList]); public selectRoles(selected: string[]): void { - this.selectedRoleKeys = selected; + this.selectedRoleKeys.set(selected); } public closeDialog(): void { - this.dialogRef.close(false); + this.dialogRef.close(); } public closeDialogWithSuccess(): void { - this.dialogRef.close({ roles: this.selectedRoleKeys }); + this.dialogRef.close({ roles: this.selectedRoleKeys() }); } } diff --git a/console/src/app/modules/user-grants/user-grants.component.ts b/console/src/app/modules/user-grants/user-grants.component.ts index 60fd38ce5..45af6c04b 100644 --- a/console/src/app/modules/user-grants/user-grants.component.ts +++ b/console/src/app/modules/user-grants/user-grants.component.ts @@ -5,6 +5,7 @@ import { MatInput } from '@angular/material/input'; import { MatTable } from '@angular/material/table'; import { Router } from '@angular/router'; import { tap } from 'rxjs/operators'; +import { RpcError } from 'grpc-web'; import { enterAnimations } from 'src/app/animations'; import { UserGrant as AuthUserGrant } from 'src/app/proto/generated/zitadel/auth_pb'; import { @@ -20,7 +21,11 @@ import { ToastService } from 'src/app/services/toast.service'; import { ActionKeysType } from '../action-keys/action-keys.component'; import { PageEvent, PaginatorComponent } from '../paginator/paginator.component'; -import { UserGrantRoleDialogComponent } from '../user-grant-role-dialog/user-grant-role-dialog.component'; +import { + UserGrantRoleDialogComponent, + UserGrantRoleDialogData, + UserGrantRoleDialogResult, +} from '../user-grant-role-dialog/user-grant-role-dialog.component'; import { WarnDialogComponent } from '../warn-dialog/warn-dialog.component'; import { UserGrantContext, UserGrantsDataSource } from './user-grants-datasource'; import { Org } from 'src/app/proto/generated/zitadel/org_pb'; @@ -176,32 +181,40 @@ export class UserGrantsComponent implements OnInit, AfterViewInit { } public openEditDialog(grant: UserGrantAsObject): void { - const dialogRef = this.dialog.open(UserGrantRoleDialogComponent, { - data: { - projectId: grant.projectId, - grantId: grant?.projectGrantId, - selectedRoleKeysList: grant.roleKeysList, - i18nTitle: 'GRANTS.EDIT.TITLE', + const dialogRef = this.dialog.open( + UserGrantRoleDialogComponent, + { + data: { + projectId: grant.projectId, + grantId: grant?.projectGrantId, + selectedRoleKeysList: grant.roleKeysList, + i18nTitle: 'GRANTS.EDIT.TITLE', + }, + width: '600px', }, - width: '600px', - }); + ); dialogRef.afterClosed().subscribe((resp) => { - if (resp && resp.roles) { - this.userService - .updateUserGrant( - (grant as MgmtUserGrant.AsObject).id ?? (grant as AuthUserGrant.AsObject).grantId, - grant.userId, - resp.roles, - ) - .then(() => { - this.toast.showInfo('GRANTS.TOAST.UPDATED', true); - grant.roleKeysList = resp.roles; - }) - .catch((error) => { - this.toast.showError(error); - }); + if (!resp || !resp.roles) { + return; } + this.userService + .updateUserGrant( + (grant as MgmtUserGrant.AsObject).id ?? (grant as AuthUserGrant.AsObject).grantId, + grant.userId, + resp.roles, + ) + .then(() => { + this.toast.showInfo('GRANTS.TOAST.UPDATED', true); + grant.roleKeysList = resp.roles; + }) + .catch((error) => { + // Errors.UserGrant.NotChanged + if (error instanceof RpcError && error.message.includes('COMMAND-Rs8fy')) { + return; + } + this.toast.showError(error); + }); }); } diff --git a/console/src/app/pages/projects/owned-projects/project-grant-detail/project-grant-detail.component.ts b/console/src/app/pages/projects/owned-projects/project-grant-detail/project-grant-detail.component.ts index 1353c35f4..ba3dac6e8 100644 --- a/console/src/app/pages/projects/owned-projects/project-grant-detail/project-grant-detail.component.ts +++ b/console/src/app/pages/projects/owned-projects/project-grant-detail/project-grant-detail.component.ts @@ -2,16 +2,20 @@ import { Component, EventEmitter } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; import { PageEvent } from '@angular/material/paginator'; import { ActivatedRoute } from '@angular/router'; +import { RpcError } from 'grpc-web'; import { ActionKeysType } from 'src/app/modules/action-keys/action-keys.component'; import { CreationType, MemberCreateDialogComponent } from 'src/app/modules/add-member-dialog/member-create-dialog.component'; -import { UserGrantRoleDialogComponent } from 'src/app/modules/user-grant-role-dialog/user-grant-role-dialog.component'; +import { + UserGrantRoleDialogComponent, + UserGrantRoleDialogData, + UserGrantRoleDialogResult, +} from 'src/app/modules/user-grant-role-dialog/user-grant-role-dialog.component'; import { Member } from 'src/app/proto/generated/zitadel/member_pb'; import { GrantedProject, ProjectGrantState, Role } from 'src/app/proto/generated/zitadel/project_pb'; import { User } from 'src/app/proto/generated/zitadel/user_pb'; import { Breadcrumb, BreadcrumbService, BreadcrumbType } from 'src/app/services/breadcrumb.service'; import { ManagementService } from 'src/app/services/mgmt.service'; import { ToastService } from 'src/app/services/toast.service'; - import { ProjectGrantMembersDataSource } from './project-grant-members-datasource'; @Component({ @@ -260,31 +264,38 @@ export class ProjectGrantDetailComponent { } public editRoles(): void { - const dialogRef = this.dialog.open(UserGrantRoleDialogComponent, { - data: { - projectId: this.grant.projectId, - selectedRoleKeysList: this.grant.grantedRoleKeysList, - i18nTitle: 'PROJECT.GRANT.EDITTITLE', + const dialogRef = this.dialog.open( + UserGrantRoleDialogComponent, + { + data: { + projectId: this.grant.projectId, + selectedRoleKeysList: this.grant.grantedRoleKeysList, + i18nTitle: 'PROJECT.GRANT.EDITTITLE', + }, + width: '600px', }, - width: '600px', - }); + ); dialogRef.afterClosed().subscribe((resp) => { - if (resp && resp.roles) { - this.mgmtService - .updateProjectGrant(this.grant.grantId, this.grant.projectId, resp.roles) - .then(() => { - this.toast.showInfo('PROJECT.GRANT.TOAST.PROJECTGRANTUPDATED', true); - this.grant.grantedRoleKeysList = resp.roles; - setTimeout(() => { - this.changePage.emit(); - }, 1000); - }) - .catch((error) => { - this.changePage.emit(); - this.toast.showError(error); - }); + if (!resp || !resp.roles) { + return; } + this.mgmtService + .updateProjectGrant(this.grant.grantId, this.grant.projectId, resp.roles) + .then(() => { + this.toast.showInfo('PROJECT.GRANT.TOAST.PROJECTGRANTUPDATED', true); + this.grant.grantedRoleKeysList = resp.roles; + setTimeout(() => { + this.changePage.emit(); + }, 1000); + }) + .catch((error) => { + // Errors.UserGrant.NotChanged + if (!(error instanceof RpcError && error.message.includes('COMMAND-Rs8fy'))) { + this.toast.showError(error); + } + this.changePage.emit(); + }); }); } }