mirror of
https://github.com/zitadel/zitadel.git
synced 2024-12-13 03:24:26 +00:00
feat(console): refresh roles, user-grants and project-members based on previous navigation (#728)
* feat: refresh roles, user-grants, project-members * refresh keys * user grants on granted projects * refresh on member changes * membership refresh * lint
This commit is contained in:
parent
15c977dab4
commit
4926509de0
@ -38,6 +38,7 @@ import { GrpcService } from './services/grpc.service';
|
||||
import { AuthInterceptor } from './services/interceptors/auth.interceptor';
|
||||
import { GRPC_INTERCEPTORS } from './services/interceptors/grpc-interceptor';
|
||||
import { OrgInterceptor } from './services/interceptors/org.interceptor';
|
||||
import { RefreshService } from './services/refresh.service';
|
||||
import { StatehandlerProcessorService, StatehandlerProcessorServiceImpl } from './services/statehandler-processor.service';
|
||||
import { StatehandlerService, StatehandlerServiceImpl } from './services/statehandler.service';
|
||||
import { StorageService } from './services/storage.service';
|
||||
@ -154,6 +155,7 @@ const authConfig: AuthConfig = {
|
||||
multi: true,
|
||||
useClass: OrgInterceptor,
|
||||
},
|
||||
RefreshService,
|
||||
GrpcService,
|
||||
GrpcAuthService,
|
||||
{ provide: 'windowObject', useValue: window },
|
||||
|
@ -103,7 +103,11 @@ export class ProjectMembersComponent {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
}));
|
||||
})).then(() => {
|
||||
setTimeout(() => {
|
||||
this.changePage();
|
||||
}, 1000);
|
||||
});
|
||||
}
|
||||
|
||||
public isAllSelected(): boolean {
|
||||
@ -141,6 +145,9 @@ export class ProjectMembersComponent {
|
||||
user.id, roles);
|
||||
}
|
||||
})).then(() => {
|
||||
setTimeout(() => {
|
||||
this.changePage();
|
||||
}, 1000);
|
||||
this.toast.showInfo('PROJECT.TOAST.MEMBERSADDED', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
|
@ -1,5 +1,6 @@
|
||||
<app-refresh-table *ngIf="projectId" (refreshed)="refreshPage()" [dataSize]="dataSource?.totalResult"
|
||||
[selection]="selection" [loading]="dataSource?.loading$ | async" [timestamp]="dataSource?.viewTimestamp">
|
||||
emitRefreshOnPreviousRoute="/projects/{{projectId}}/roles/create" [selection]="selection"
|
||||
[loading]="dataSource?.loading$ | async" [timestamp]="dataSource?.viewTimestamp">
|
||||
<ng-template appHasRole [appHasRole]="['project.role.delete', 'project.role.delete:' + projectId]" actions>
|
||||
<button color="warn" class="icon-button" [disabled]="disabled"
|
||||
matTooltip="{{'PROJECT.ROLE.DELETE' | translate}}" (click)="deleteSelectedRoles()" mat-icon-button
|
||||
|
@ -2,6 +2,7 @@ import { animate, animation, keyframes, style, transition, trigger, useAnimation
|
||||
import { SelectionModel } from '@angular/cdk/collections';
|
||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb';
|
||||
import { RefreshService } from 'src/app/services/refresh.service';
|
||||
|
||||
const rotate = animation([
|
||||
animate(
|
||||
@ -32,14 +33,24 @@ export class RefreshTableComponent implements OnInit {
|
||||
@Input() public dataSize: number = 0;
|
||||
@Input() public emitRefreshAfterTimeoutInMs: number = 0;
|
||||
@Input() public loading: boolean = false;
|
||||
@Input() public emitRefreshOnPreviousRoute: string = '';
|
||||
@Output() public refreshed: EventEmitter<void> = new EventEmitter();
|
||||
|
||||
constructor(private refreshService: RefreshService) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
if (this.emitRefreshAfterTimeoutInMs) {
|
||||
setTimeout(() => {
|
||||
this.emitRefresh();
|
||||
}, this.emitRefreshAfterTimeoutInMs);
|
||||
}
|
||||
|
||||
if (this.emitRefreshOnPreviousRoute && this.refreshService.previousUrls.includes(this.emitRefreshOnPreviousRoute)) {
|
||||
setTimeout(() => {
|
||||
console.log('refresh now');
|
||||
this.emitRefresh();
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
emitRefresh(): void {
|
||||
|
@ -1,5 +1,6 @@
|
||||
<app-refresh-table [loading]="dataSource?.loading$ | async" (refreshed)="changePage()"
|
||||
[timestamp]="dataSource?.viewTimestamp" [dataSize]="dataSource?.totalResult" [selection]="selection">
|
||||
[emitRefreshOnPreviousRoute]="refreshOnPreviousRoute" [timestamp]="dataSource?.viewTimestamp"
|
||||
[dataSize]="dataSource?.totalResult" [selection]="selection">
|
||||
<button color="warn" matTooltip="{{'GRANTS.DELETE' | translate}}" class="icon-button" mat-icon-button actions
|
||||
(click)="deleteGrantSelection()" *ngIf="selection.hasValue() && allowDelete">
|
||||
<i class="las la-trash"></i>
|
||||
@ -21,9 +22,8 @@
|
||||
<td class="selection" mat-cell *matCellDef="let row">
|
||||
<mat-checkbox color="primary" (click)="$event.stopPropagation()"
|
||||
(change)="$event ? selection.toggle(row) : null" [checked]="selection.isSelected(row)">
|
||||
<app-avatar *ngIf="row && (row?.displayName || (row.firstName && row.lastName))" class="avatar"
|
||||
[name]="row.displayName ? row.displayName : (row.firstName + ' '+ row.lastName)"
|
||||
[size]="32">
|
||||
<app-avatar *ngIf="row && row?.displayName && row.firstName && row.lastName" class="avatar"
|
||||
[name]="row.displayName" [size]="32">
|
||||
</app-avatar>
|
||||
</mat-checkbox>
|
||||
</td>
|
||||
@ -33,7 +33,7 @@
|
||||
<ng-container matColumnDef="user">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.GRANT.USER' | translate }} </th>
|
||||
<td class="pointer" mat-cell *matCellDef="let grant">
|
||||
{{grant?.firstName}} {{grant?.lastName}}</td>
|
||||
{{grant?.displayName}}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="org">
|
||||
|
@ -24,6 +24,7 @@ import { UserGrantContext, UserGrantsDataSource } from './user-grants-datasource
|
||||
})
|
||||
export class UserGrantsComponent implements OnInit, AfterViewInit {
|
||||
@Input() context: UserGrantContext = UserGrantContext.USER;
|
||||
@Input() refreshOnPreviousRoute: string = '';
|
||||
public grants: UserGrantView.AsObject[] = [];
|
||||
|
||||
public dataSource!: UserGrantsDataSource;
|
||||
|
@ -85,6 +85,9 @@ export class IamMembersComponent implements AfterViewInit {
|
||||
public removeMember(member: ProjectMember.AsObject): void {
|
||||
this.adminService.RemoveIamMember(member.userId).then(() => {
|
||||
this.toast.showInfo('IAM.TOAST.MEMBERREMOVED', true);
|
||||
setTimeout(() => {
|
||||
this.refreshPage();
|
||||
}, 1000);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
@ -120,6 +123,9 @@ export class IamMembersComponent implements AfterViewInit {
|
||||
return this.adminService.AddIamMember(user.id, roles);
|
||||
})).then(() => {
|
||||
this.toast.showInfo('IAM.TOAST.MEMBERADDED', true);
|
||||
setTimeout(() => {
|
||||
this.refreshPage();
|
||||
}, 1000);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
|
@ -57,6 +57,9 @@ export class IamComponent {
|
||||
return this.adminService.AddIamMember(user.id, roles);
|
||||
})).then(() => {
|
||||
this.toast.showInfo('IAM.TOAST.MEMBERADDED');
|
||||
setTimeout(() => {
|
||||
this.loadMembers();
|
||||
}, 1000);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
|
@ -180,6 +180,9 @@ export class OrgDetailComponent implements OnInit, OnDestroy {
|
||||
return this.mgmtService.AddMyOrgMember(user.id, roles);
|
||||
})).then(() => {
|
||||
this.toast.showInfo('ORG.TOAST.MEMBERADDED', true);
|
||||
setTimeout(() => {
|
||||
this.loadMembers();
|
||||
}, 1000);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
|
@ -82,7 +82,11 @@ export class OrgMembersComponent implements AfterViewInit {
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}));
|
||||
})).then(() => {
|
||||
setTimeout(() => {
|
||||
this.refreshPage();
|
||||
}, 1000);
|
||||
});
|
||||
}
|
||||
|
||||
public isAllSelected(): boolean {
|
||||
@ -115,6 +119,9 @@ export class OrgMembersComponent implements AfterViewInit {
|
||||
return this.mgmtService.AddMyOrgMember(user.id, roles);
|
||||
})).then(() => {
|
||||
this.toast.showInfo('ORG.TOAST.MEMBERADDED', true);
|
||||
setTimeout(() => {
|
||||
this.refreshPage();
|
||||
}, 1000);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
|
@ -19,7 +19,8 @@
|
||||
[projectId]="projectId" [grantId]="grantId"
|
||||
[displayedColumns]="['select','user', 'projectId', 'creationDate', 'changeDate', 'roleNamesList']"
|
||||
[allowCreate]="['user.grant.write','user.grant.write:'+grantId] | hasRole | async"
|
||||
[allowDelete]="['user.grant.delete','user.grant.delete:'+grantId] | hasRole | async">
|
||||
[allowDelete]="['user.grant.delete','user.grant.delete:'+grantId] | hasRole | async"
|
||||
refreshOnPreviousRoute="/grant-create/project/{{projectId}}/grant/{{grantId}}">
|
||||
</app-user-grants>
|
||||
</app-card>
|
||||
</ng-template>
|
||||
|
@ -152,6 +152,9 @@ export class GrantedProjectDetailComponent implements OnInit, OnDestroy {
|
||||
roles,
|
||||
).then(() => {
|
||||
this.toast.showInfo('PROJECT.TOAST.MEMBERADDED', true);
|
||||
setTimeout(() => {
|
||||
this.loadMembers();
|
||||
}, 1000);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
|
@ -68,7 +68,7 @@
|
||||
[appHasRole]="['project.grant.read:' + project.projectId, 'project.grant.read']">
|
||||
<app-card title="{{ 'PROJECT.GRANT.TITLE' | translate }}"
|
||||
description="{{ 'PROJECT.GRANT.DESCRIPTION' | translate }}">
|
||||
<app-project-grants
|
||||
<app-project-grants refreshOnPreviousRoute="/projects/{{projectId}}/grants/create"
|
||||
[disabled]="((['project.grant.write', 'project.grant.write:'+ project.projectId]| hasRole | async) && (['org.global.read']| hasRole | async))== false"
|
||||
[projectId]="projectId">
|
||||
</app-project-grants>
|
||||
@ -89,6 +89,7 @@
|
||||
<app-card *ngIf="project?.projectId" title="{{ 'GRANTS.PROJECT.TITLE' | translate }}"
|
||||
description="{{'GRANTS.PROJECT.DESCRIPTION' | translate }}">
|
||||
<app-user-grants [context]="UserGrantContext.OWNED_PROJECT" [projectId]="projectId"
|
||||
refreshOnPreviousRoute="/grant-create/project/{{projectId}}"
|
||||
[allowCreate]="(['user.grant.write', 'user.grant.write:'+projectId] | hasRole) | async"
|
||||
[allowDelete]="(['user.grant.delete','user.grant.delete:'+projectId] | hasRole) | async">
|
||||
</app-user-grants>
|
||||
|
@ -226,6 +226,9 @@ export class OwnedProjectDetailComponent implements OnInit, OnDestroy {
|
||||
return this.mgmtService.AddProjectMember(this.projectId, user.id, roles)
|
||||
.then(() => {
|
||||
this.toast.showInfo('PROJECT.TOAST.MEMBERADDED', true);
|
||||
setTimeout(() => {
|
||||
this.loadMembers();
|
||||
}, 1000);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
|
@ -1,5 +1,6 @@
|
||||
<app-refresh-table [loading]="dataSource?.loading$ | async" *ngIf="projectId" (refreshed)="loadGrantsPage()"
|
||||
[dataSize]="dataSource.totalResult" [selection]="selection" [timestamp]="dataSource?.viewTimestamp">
|
||||
[dataSize]="dataSource.totalResult" [selection]="selection" [timestamp]="dataSource?.viewTimestamp"
|
||||
[emitRefreshOnPreviousRoute]="refreshOnPreviousRoute">
|
||||
<ng-template appHasRole [appHasRole]="['project.grant.member.delete:'+projectId, 'project.grant.member.delete']"
|
||||
actions>
|
||||
<button (click)="deleteSelectedGrants()" [disabled]="disabled" mat-icon-button *ngIf="selection.hasValue()"
|
||||
|
@ -24,6 +24,7 @@ import { ProjectGrantsDataSource } from './project-grants-datasource';
|
||||
],
|
||||
})
|
||||
export class ProjectGrantsComponent implements OnInit, AfterViewInit {
|
||||
@Input() refreshOnPreviousRoute: string = '';
|
||||
@Input() public projectId: string = '';
|
||||
@Input() public disabled: boolean = false;
|
||||
@ViewChild(MatPaginator) public paginator!: MatPaginator;
|
||||
|
@ -100,7 +100,10 @@ export class MachineKeysComponent implements OnInit {
|
||||
console.log(this.userId, type, date);
|
||||
return this.userService.AddMachineKey(this.userId, type, date).then((response) => {
|
||||
if (response) {
|
||||
console.log(response.toObject());
|
||||
setTimeout(() => {
|
||||
this.refreshPage();
|
||||
}, 1000);
|
||||
|
||||
this.dialog.open(ShowKeyDialogComponent, {
|
||||
data: {
|
||||
key: response.toObject(),
|
||||
|
@ -132,6 +132,9 @@ export class MembershipDetailComponent implements AfterViewInit {
|
||||
return this.adminService.AddIamMember(user.id, roles);
|
||||
})).then(() => {
|
||||
this.toast.showInfo('IAM.TOAST.MEMBERADDED', true);
|
||||
setTimeout(() => {
|
||||
this.refreshPage();
|
||||
}, 1000);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
@ -147,6 +150,9 @@ export class MembershipDetailComponent implements AfterViewInit {
|
||||
return this.mgmtService.AddMyOrgMember(user.id, roles);
|
||||
})).then(() => {
|
||||
this.toast.showInfo('ORG.TOAST.MEMBERADDED', true);
|
||||
setTimeout(() => {
|
||||
this.refreshPage();
|
||||
}, 1000);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
@ -166,6 +172,9 @@ export class MembershipDetailComponent implements AfterViewInit {
|
||||
roles,
|
||||
).then(() => {
|
||||
this.toast.showInfo('PROJECT.TOAST.MEMBERADDED', true);
|
||||
setTimeout(() => {
|
||||
this.refreshPage();
|
||||
}, 1000);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
@ -182,6 +191,9 @@ export class MembershipDetailComponent implements AfterViewInit {
|
||||
return this.mgmtService.AddProjectMember(response.projectId, user.id, roles)
|
||||
.then(() => {
|
||||
this.toast.showInfo('PROJECT.TOAST.MEMBERADDED', true);
|
||||
setTimeout(() => {
|
||||
this.refreshPage();
|
||||
}, 1000);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
|
@ -32,6 +32,11 @@
|
||||
aria-label="add membership">
|
||||
<mat-icon>add</mat-icon>
|
||||
</button>
|
||||
<span class="fill-space"></span>
|
||||
<button class="refresh-img" (click)="loadManager(user.id)" [disabled]="disabled" mat-icon-button
|
||||
aria-label="refresh contributors">
|
||||
<mat-icon>refresh</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -48,6 +48,15 @@
|
||||
margin: 0 8px 0 -15px;
|
||||
}
|
||||
|
||||
.fill-space {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.refresh-img {
|
||||
float: left;
|
||||
margin: 0 8px 0 -15px;
|
||||
}
|
||||
|
||||
.avatar-circle {
|
||||
float: left;
|
||||
margin: 0 8px 0 -12px;
|
||||
|
@ -100,6 +100,9 @@ export class MembershipsComponent implements OnInit {
|
||||
return this.adminService.AddIamMember(user.id, roles);
|
||||
})).then(() => {
|
||||
this.toast.showInfo('IAM.TOAST.MEMBERADDED', true);
|
||||
setTimeout(() => {
|
||||
this.loadManager(this.user.id);
|
||||
}, 1000);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
@ -115,6 +118,9 @@ export class MembershipsComponent implements OnInit {
|
||||
return this.mgmtService.AddMyOrgMember(user.id, roles);
|
||||
})).then(() => {
|
||||
this.toast.showInfo('ORG.TOAST.MEMBERADDED', true);
|
||||
setTimeout(() => {
|
||||
this.loadManager(this.user.id);
|
||||
}, 1000);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
@ -134,6 +140,9 @@ export class MembershipsComponent implements OnInit {
|
||||
roles,
|
||||
).then(() => {
|
||||
this.toast.showInfo('PROJECT.TOAST.MEMBERADDED', true);
|
||||
setTimeout(() => {
|
||||
this.loadManager(this.user.id);
|
||||
}, 1000);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
@ -150,6 +159,9 @@ export class MembershipsComponent implements OnInit {
|
||||
return this.mgmtService.AddProjectMember(response.projectId, user.id, roles)
|
||||
.then(() => {
|
||||
this.toast.showInfo('PROJECT.TOAST.MEMBERADDED', true);
|
||||
setTimeout(() => {
|
||||
this.loadManager(this.user.id);
|
||||
}, 1000);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
|
@ -1,5 +1,6 @@
|
||||
<app-refresh-table [loading]="loading$ | async" (refreshed)="refreshPage()" [dataSize]="dataSource.data.length"
|
||||
[timestamp]="userResult?.viewTimestamp" [selection]="selection">
|
||||
[timestamp]="userResult?.viewTimestamp" [selection]="selection"
|
||||
[emitRefreshOnPreviousRoute]="refreshOnPreviousRoute">
|
||||
<ng-template appHasRole [appHasRole]="['user.write']" actions>
|
||||
<button (click)="deactivateSelectedUsers()" matTooltip="{{'ORG_DETAIL.TABLE.DEACTIVATE' | translate}}"
|
||||
class="icon-button" mat-icon-button *ngIf="selection.hasValue()" [disabled]="disabled">
|
||||
|
@ -19,6 +19,7 @@ import { UserType } from '../user-list.component';
|
||||
export class UserTableComponent implements OnInit {
|
||||
public UserType: any = UserType;
|
||||
@Input() userType: UserType = UserType.HUMAN;
|
||||
@Input() refreshOnPreviousRoute: string = '';
|
||||
@Input() disabled: boolean = false;
|
||||
@ViewChild(MatPaginator) public paginator!: MatPaginator;
|
||||
public dataSource: MatTableDataSource<UserView.AsObject> = new MatTableDataSource<UserView.AsObject>();
|
||||
|
27
console/src/app/services/refresh.service.ts
Normal file
27
console/src/app/services/refresh.service.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Event, NavigationEnd, Router } from '@angular/router';
|
||||
import { filter } from 'rxjs/operators';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class RefreshService {
|
||||
private fifo: Array<string> = ['', ''];
|
||||
constructor(router: Router) {
|
||||
router.events
|
||||
.pipe(filter(event => event instanceof NavigationEnd))
|
||||
.subscribe((event: Event | any) => {
|
||||
console.log('prev:', event.url);
|
||||
this.moveInto(event.url);
|
||||
});
|
||||
}
|
||||
|
||||
public get previousUrls(): Array<string> {
|
||||
return this.fifo;
|
||||
}
|
||||
|
||||
private moveInto(value: string): void {
|
||||
this.fifo[1] = this.fifo[0];
|
||||
this.fifo[0] = value;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user