mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-11 21:17:32 +00:00
feat(console): delete user (#819)
* add action col to user table, i18n * delete user from detail component * lint
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
<span class="title" mat-dialog-title>{{data.titleKey | translate}}</span>
|
||||
<span class="title" mat-dialog-title>{{data.titleKey | translate: data.titleParam}}</span>
|
||||
<div mat-dialog-content>
|
||||
<p class="desc"> {{data.descriptionKey | translate}}</p>
|
||||
<p class="desc"> {{data.descriptionKey | translate: data.descriptionParam}}</p>
|
||||
</div>
|
||||
<div mat-dialog-actions class="action">
|
||||
<button *ngIf="data.cancelKey" mat-button (click)="closeDialog()">
|
||||
|
@@ -49,7 +49,6 @@ export class IamViewsComponent implements AfterViewInit {
|
||||
}
|
||||
|
||||
public cancelView(viewname: string, db: string): void {
|
||||
|
||||
const dialogRef = this.dialog.open(WarnDialogComponent, {
|
||||
data: {
|
||||
confirmKey: 'ACTIONS.CLEAR',
|
||||
|
@@ -8,10 +8,17 @@
|
||||
|
||||
<span class="fill-space"></span>
|
||||
|
||||
<ng-template appHasRole [appHasRole]="['user.write', 'user.write:'+user?.id]">
|
||||
<button mat-stroked-button color="warn" *ngIf="user?.state === UserState.USERSTATE_ACTIVE"
|
||||
<ng-template appHasRole [appHasRole]="['user.delete$', 'user.delete:'+user?.id]">
|
||||
<button mat-raised-button color="warn" (click)="deleteUser()"><i
|
||||
class="las la-trash"></i>{{'USER.PAGES.DELETE' | translate}}</button>
|
||||
</ng-template>
|
||||
|
||||
<ng-template appHasRole [appHasRole]="['user.write$', 'user.write:'+user?.id]">
|
||||
<button class="state-button" mat-stroked-button color="warn"
|
||||
*ngIf="user?.state === UserState.USERSTATE_ACTIVE"
|
||||
(click)="changeState(UserState.USERSTATE_INACTIVE)">{{'USER.PAGES.DEACTIVATE' | translate}}</button>
|
||||
<button mat-stroked-button color="warn" *ngIf="user?.state === UserState.USERSTATE_INACTIVE"
|
||||
<button class="state-button" mat-stroked-button color="warn"
|
||||
*ngIf="user?.state === UserState.USERSTATE_INACTIVE"
|
||||
(click)="changeState(UserState.USERSTATE_ACTIVE)">{{'USER.PAGES.REACTIVATE' | translate}}</button>
|
||||
</ng-template>
|
||||
</div>
|
||||
|
@@ -17,6 +17,10 @@
|
||||
.fill-space {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.state-button {
|
||||
margin-left: .5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.method-col {
|
||||
|
@@ -1,9 +1,11 @@
|
||||
import { Location } from '@angular/common';
|
||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { ChangeType } from 'src/app/modules/changes/changes.component';
|
||||
import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component';
|
||||
import {
|
||||
Gender,
|
||||
MachineResponse,
|
||||
@@ -44,6 +46,7 @@ export class UserDetailComponent implements OnInit, OnDestroy {
|
||||
private toast: ToastService,
|
||||
public mgmtUserService: ManagementService,
|
||||
private _location: Location,
|
||||
private dialog: MatDialog,
|
||||
) { }
|
||||
|
||||
public ngOnInit(): void {
|
||||
@@ -195,4 +198,29 @@ export class UserDetailComponent implements OnInit, OnDestroy {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
|
||||
public deleteUser(): void {
|
||||
const dialogRef = this.dialog.open(WarnDialogComponent, {
|
||||
data: {
|
||||
confirmKey: 'ACTIONS.DELETE',
|
||||
cancelKey: 'ACTIONS.CANCEL',
|
||||
titleKey: 'USER.DIALOG.DELETE_TITLE',
|
||||
descriptionParam: this.user.human ??
|
||||
this.user.machine ? { displayName: this.user.machine?.name } : { displayName: '' },
|
||||
descriptionKey: 'USER.DIALOG.DELETE_DESCRIPTION',
|
||||
},
|
||||
width: '400px',
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe(resp => {
|
||||
if (resp) {
|
||||
this.mgmtUserService.DeleteUser(this.user.id).then(() => {
|
||||
this.navigateBack();
|
||||
this.toast.showInfo('USER.TOAST.DELETED', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -12,7 +12,7 @@
|
||||
<p class="sub">{{ 'USER.PAGES.DESCRIPTIONMACHINE' | translate }}</p>
|
||||
|
||||
<app-user-table [userType]="UserType.MACHINE"
|
||||
[displayedColumns]="['select','name', 'username', 'description','state']"
|
||||
[displayedColumns]="['select','name', 'username', 'description','state', 'actions']"
|
||||
[disabled]="(['user.write'] | hasRole | async) == false">
|
||||
</app-user-table>
|
||||
</ng-container>
|
||||
|
@@ -48,7 +48,8 @@
|
||||
<template [ngTemplateOutlet]="templateRef"
|
||||
[ngTemplateOutletContext]="{key: UserSearchKey.USERSEARCHKEY_FIRST_NAME}"></template>
|
||||
</th>
|
||||
<td mat-cell *matCellDef="let user"> {{user[userType]?.firstName}} </td>
|
||||
<td mat-cell *matCellDef="let user" [routerLink]="user.id ? ['/users', user.id ]: null">
|
||||
{{user[userType]?.firstName}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="lastname">
|
||||
@@ -58,7 +59,8 @@
|
||||
<template [ngTemplateOutlet]="templateRef"
|
||||
[ngTemplateOutletContext]="{key: UserSearchKey.USERSEARCHKEY_LAST_NAME}"></template>
|
||||
</th>
|
||||
<td mat-cell *matCellDef="let user"> {{user[userType]?.lastName}} </td>
|
||||
<td mat-cell *matCellDef="let user" [routerLink]="user.id ? ['/users', user.id ]: null">
|
||||
{{user[userType]?.lastName}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="displayName">
|
||||
@@ -68,19 +70,22 @@
|
||||
<template [ngTemplateOutlet]="templateRef"
|
||||
[ngTemplateOutletContext]="{key: UserSearchKey.USERSEARCHKEY_DISPLAY_NAME}"></template>
|
||||
</th>
|
||||
<td mat-cell *matCellDef="let user"> {{user[userType]?.displayName}} </td>
|
||||
<td mat-cell *matCellDef="let user" [routerLink]="user.id ? ['/users', user.id ]: null">
|
||||
{{user[userType]?.displayName}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="name">
|
||||
<th mat-header-cell *matHeaderCellDef>
|
||||
{{ 'USER.MACHINE.NAME' | translate }}
|
||||
</th>
|
||||
<td mat-cell *matCellDef="let user"> {{user[userType]?.name}} </td>
|
||||
<td mat-cell *matCellDef="let user" [routerLink]="user.id ? ['/users', user.id ]: null">
|
||||
{{user[userType]?.name}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="description">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ 'USER.MACHINE.DESCRIPTION' | translate }} </th>
|
||||
<td mat-cell *matCellDef="let user"> {{user[userType]?.description}} </td>
|
||||
<td mat-cell *matCellDef="let user" [routerLink]="user.id ? ['/users', user.id ]: null">
|
||||
{{user[userType]?.description}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="username">
|
||||
@@ -90,7 +95,8 @@
|
||||
<template [ngTemplateOutlet]="templateRef"
|
||||
[ngTemplateOutletContext]="{key: UserSearchKey.USERSEARCHKEY_USER_NAME}"></template>
|
||||
</th>
|
||||
<td mat-cell *matCellDef="let user"> {{user.userName}} </td>
|
||||
<td mat-cell *matCellDef="let user" [routerLink]="user.id ? ['/users', user.id ]: null">
|
||||
{{user.userName}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="email">
|
||||
@@ -100,17 +106,29 @@
|
||||
<template [ngTemplateOutlet]="templateRef"
|
||||
[ngTemplateOutletContext]="{key: UserSearchKey.USERSEARCHKEY_EMAIL}"></template>
|
||||
</th>
|
||||
<td mat-cell *matCellDef="let user"> {{user[userType]?.email}} </td>
|
||||
<td mat-cell *matCellDef="let user" [routerLink]="user.id ? ['/users', user.id ]: null">
|
||||
{{user[userType]?.email}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="state">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ 'USER.DATA.STATE' | translate }} </th>
|
||||
<td mat-cell *matCellDef="let user"> {{ 'USER.DATA.STATE'+user.state | translate }} </td>
|
||||
<td mat-cell *matCellDef="let user" [routerLink]="user.id ? ['/users', user.id ]: null">
|
||||
{{ 'USER.DATA.STATE'+user.state | translate }} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="actions" stickyEnd>
|
||||
<th mat-header-cell *matHeaderCellDef></th>
|
||||
<td mat-cell *matCellDef="let user">
|
||||
<button [disabled]="(['user.delete$', 'user.delete:'+user.id] | hasRole | async) == false"
|
||||
color="warn" mat-icon-button matTooltip="{{'IAM.VIEWS.DELETE' | translate}}"
|
||||
(click)="deleteUser(user)">
|
||||
<i class="las la-trash"></i>
|
||||
</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr class="highlight" mat-row *matRowDef="let row; columns: displayedColumns;"
|
||||
[routerLink]="row.id ? ['/users', row.id ]: null">
|
||||
</tr>
|
||||
<tr class="highlight" mat-row *matRowDef="let row; columns: displayedColumns;"> </tr>
|
||||
|
||||
</table>
|
||||
<mat-paginator #paginator class="paginator" [length]="userResult?.totalResult || 0" [pageSize]="10"
|
||||
|
@@ -24,6 +24,16 @@
|
||||
|
||||
tr {
|
||||
outline: none;
|
||||
|
||||
button {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
button {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
th {
|
||||
|
@@ -1,10 +1,12 @@
|
||||
import { SelectionModel } from '@angular/cdk/collections';
|
||||
import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { MatPaginator, PageEvent } from '@angular/material/paginator';
|
||||
import { MatTableDataSource } from '@angular/material/table';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
import { enterAnimations } from 'src/app/animations';
|
||||
import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component';
|
||||
import { UserView } from 'src/app/proto/generated/auth_pb';
|
||||
import { SearchMethod, UserSearchKey, UserSearchQuery, UserSearchResponse } from 'src/app/proto/generated/management_pb';
|
||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
@@ -32,12 +34,16 @@ export class UserTableComponent implements OnInit {
|
||||
public userResult!: UserSearchResponse.AsObject;
|
||||
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||
public loading$: Observable<boolean> = this.loadingSubject.asObservable();
|
||||
@Input() public displayedColumns: string[] = ['select', /*'firstname', 'lastname' ,*/ 'displayName', 'username', 'email', 'state'];
|
||||
@Input() public displayedColumns: string[] = ['select', 'displayName', 'username', 'email', 'state', 'actions'];
|
||||
|
||||
@Output() public changedSelection: EventEmitter<Array<UserView.AsObject>> = new EventEmitter();
|
||||
UserSearchKey: any = UserSearchKey;
|
||||
constructor(public translate: TranslateService, private userService: ManagementService,
|
||||
private toast: ToastService) {
|
||||
constructor(
|
||||
public translate: TranslateService,
|
||||
private userService: ManagementService,
|
||||
private toast: ToastService,
|
||||
private dialog: MatDialog,
|
||||
) {
|
||||
this.selection.changed.subscribe(() => {
|
||||
this.changedSelection.emit(this.selection.selected);
|
||||
});
|
||||
@@ -129,4 +135,30 @@ export class UserTableComponent implements OnInit {
|
||||
this.refreshPage();
|
||||
}
|
||||
}
|
||||
|
||||
public deleteUser(user: UserView.AsObject): void {
|
||||
const dialogRef = this.dialog.open(WarnDialogComponent, {
|
||||
data: {
|
||||
confirmKey: 'ACTIONS.DELETE',
|
||||
cancelKey: 'ACTIONS.CANCEL',
|
||||
titleKey: 'USER.DIALOG.DELETE_TITLE',
|
||||
descriptionParam: user.human ?? user.machine ? { displayName: user.machine?.name } : { displayName: '' },
|
||||
descriptionKey: 'USER.DIALOG.DELETE_DESCRIPTION',
|
||||
},
|
||||
width: '400px',
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe(resp => {
|
||||
if (resp) {
|
||||
this.userService.DeleteUser(user.id).then(() => {
|
||||
setTimeout(() => {
|
||||
this.refreshPage();
|
||||
}, 1000);
|
||||
this.toast.showInfo('USER.TOAST.DELETED', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -643,6 +643,12 @@ export class ManagementService {
|
||||
return this.grpcService.mgmt.getUserByID(req);
|
||||
}
|
||||
|
||||
public DeleteUser(id: string): Promise<Empty> {
|
||||
const req = new UserID();
|
||||
req.setId(id);
|
||||
return this.grpcService.mgmt.deleteUser(req);
|
||||
}
|
||||
|
||||
public SearchProjectMembers(
|
||||
projectId: string,
|
||||
limit: number,
|
||||
|
@@ -87,7 +87,12 @@
|
||||
"NOUSER":"Kein Benutzer",
|
||||
"REACTIVATE":"Reaktivieren",
|
||||
"DEACTIVATE":"Deaktivieren",
|
||||
"FILTER":"Filter"
|
||||
"FILTER":"Filter",
|
||||
"DELETE":"Benutzer löschen"
|
||||
},
|
||||
"DIALOG": {
|
||||
"DELETE_TITLE":"User löschen",
|
||||
"DELETE_DESCRIPTION":"Sie sind im Begriff den Benutzer {{displayName}} entgültig zu löschen. Wollen Sie dies wirklich tun?"
|
||||
},
|
||||
"TABLE":{
|
||||
"DEACTIVATE":"Deaktivieren",
|
||||
@@ -282,7 +287,8 @@
|
||||
"SELECTEDDEACTIVATED":"Selektierte Benutzer deaktiviert.",
|
||||
"SELECTEDKEYSDELETED":"Selektierte Schlüssel gelöscht.",
|
||||
"KEYADDED":"Schlüssel hinzugefügt!",
|
||||
"MACHINEADDED":"Service User erstellt!"
|
||||
"MACHINEADDED":"Service User erstellt!",
|
||||
"DELETED":"Benutzer erfolgreich gelöscht!"
|
||||
},
|
||||
"MEMBERSHIPS": {
|
||||
"TITLE":"ZITADEL Manager-Rollen",
|
||||
|
@@ -87,7 +87,12 @@
|
||||
"NOUSER":"No associated users.",
|
||||
"REACTIVATE":"Reactivate",
|
||||
"DEACTIVATE":"Deactivate",
|
||||
"FILTER":"Filter"
|
||||
"FILTER":"Filter",
|
||||
"DELETE":"Delete User"
|
||||
},
|
||||
"DIALOG": {
|
||||
"DELETE_TITLE":"Delete User",
|
||||
"DELETE_DESCRIPTION":"You are about to permanently delete the user {{displayName}}. Are you sure?"
|
||||
},
|
||||
"TABLE":{
|
||||
"DEACTIVATE":"Deactivate",
|
||||
@@ -282,7 +287,8 @@
|
||||
"SELECTEDDEACTIVATED":"Selected users deactivated.",
|
||||
"SELECTEDKEYSDELETED":"Selected keys deleted.",
|
||||
"KEYADDED":"Key added!",
|
||||
"MACHINEADDED":"Service User created!"
|
||||
"MACHINEADDED":"Service User created!",
|
||||
"DELETED":"User deleted successfully!"
|
||||
},
|
||||
"MEMBERSHIPS": {
|
||||
"TITLE":"ZITADEL Manager Roles",
|
||||
|
Reference in New Issue
Block a user