mirror of
https://github.com/andwati/zitadel.git
synced 2026-11-15 10:25:11 +00:00
feat(console): user grant filtering, org domain verification improvements, fix membership detail link (#916)
* user grant filter * add filter input * org domain spinner and reload * user grant filter templates * single selection filter for grants, same dl btn * filter margin * lint style, remove duplicate code * project count as observable * deferred reload on delete * fix user grant formfield * lint styles * fix event propagation on pin, change selection * propagate counter change * admin warn, localstorage, i18n, sidenav impv * overlays * adapt toolbar elevationn, card * color vars, i18n, admin section * fix lint * selection clear on filter * ts lint * Update console/src/assets/i18n/de.json Co-authored-by: Fabi <[email protected]> * Update console/src/assets/i18n/de.json Co-authored-by: Fabi <[email protected]> * Update console/src/assets/i18n/en.json Co-authored-by: Fabi <[email protected]> Co-authored-by: Fabi <[email protected]>
This commit is contained in:
+15
-8
@@ -22,11 +22,10 @@
|
||||
{{ item.creationDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm' }}</span>
|
||||
<span class="fill-space"></span>
|
||||
</div>
|
||||
<button [ngClass]="{ selected: selection.isSelected(item)}" (click)="selection.toggle(item)" class="edit-button"
|
||||
mat-icon-button>
|
||||
<mat-icon *ngIf="selection.isSelected(item)" svgIcon="mdi_pin"></mat-icon>
|
||||
<mat-icon svgIcon="mdi_pin_outline" *ngIf="!selection.isSelected(item)"></mat-icon>
|
||||
</button>
|
||||
|
||||
<template [ngTemplateOutlet]="toggleButton" [ngTemplateOutletContext]="{key: item}"></template>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -47,12 +46,20 @@
|
||||
{{ item.creationDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm' }}</span>
|
||||
<span class="fill-space"></span>
|
||||
</div>
|
||||
<button [ngClass]="{ selected: selection.isSelected(item)}" (click)="selection.toggle(item)" class="edit-button"
|
||||
mat-icon-button>
|
||||
<button [ngClass]="{ selected: selection.isSelected(item)}"
|
||||
(click)="selection.toggle(item); $event.stopPropagation()" class="edit-button" mat-icon-button>
|
||||
<mat-icon *ngIf="selection.isSelected(item)" svgIcon="mdi_pin"></mat-icon>
|
||||
<mat-icon svgIcon="mdi_pin_outline" *ngIf="!selection.isSelected(item)"></mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
<p class="n-items" *ngIf="!loading && items.length === 0 && selection.selected.length === 0">
|
||||
{{'PROJECT.PAGES.NOITEMS' | translate}}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ng-template #toggleButton let-key="key">
|
||||
<button matTooltip="{{'ACTIONS.PIN' | translate}}" [ngClass]="{ selected: selection.isSelected(key)}"
|
||||
(click)="selection.toggle(key); $event.stopPropagation()" class="edit-button" mat-icon-button>
|
||||
<mat-icon *ngIf="selection.isSelected(key)" svgIcon="mdi_pin"></mat-icon>
|
||||
<mat-icon svgIcon="mdi_pin_outline" *ngIf="!selection.isSelected(key)"></mat-icon>
|
||||
</button>
|
||||
</ng-template>
|
||||
+3
-12
@@ -47,11 +47,10 @@ export class GrantedProjectGridComponent implements OnChanges {
|
||||
this.setPrefixedItem('pinned-granted-projects', JSON.stringify(
|
||||
this.selection.selected.map(item => item.projectId),
|
||||
)).then(() => {
|
||||
const filtered = this.notPinned.filter(item => item === selection.added.find(i => i === item));
|
||||
filtered.forEach((f, i) => {
|
||||
this.notPinned.splice(i, 1);
|
||||
selection.added.forEach(item => {
|
||||
const index = this.notPinned.findIndex(i => i.projectId === item.projectId);
|
||||
this.notPinned.splice(index, 1);
|
||||
});
|
||||
|
||||
this.notPinned.push(...selection.removed);
|
||||
});
|
||||
});
|
||||
@@ -74,18 +73,10 @@ export class GrantedProjectGridComponent implements OnChanges {
|
||||
const array: string[] = JSON.parse(storageEntry);
|
||||
const toSelect: ProjectGrantView.AsObject[] = this.items.filter((item, index) => {
|
||||
if (array.includes(item.projectId)) {
|
||||
// this.notPinned.splice(index, 1);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
this.selection.select(...toSelect);
|
||||
|
||||
const toNotPinned: ProjectGrantView.AsObject[] = this.items.filter((item, index) => {
|
||||
if (!array.includes(item.projectId)) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
this.notPinned = toNotPinned;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
+4
-1
@@ -182,7 +182,10 @@ export class OwnedProjectDetailComponent implements OnInit, OnDestroy {
|
||||
if (resp) {
|
||||
this.mgmtService.RemoveProject(this.projectId).then(() => {
|
||||
this.toast.showInfo('PROJECT.TOAST.DELETED', true);
|
||||
this.router.navigate(['/projects']);
|
||||
const params: Params = {
|
||||
'deferredReload': true,
|
||||
};
|
||||
this.router.navigate(['/projects'], { queryParams: params });
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
|
||||
+22
-15
@@ -25,11 +25,9 @@
|
||||
}}</span>
|
||||
<span class="fill-space"></span>
|
||||
</div>
|
||||
<button [ngClass]="{ selected: selection.isSelected(item)}" (click)="selection.toggle(item)" class="edit-button"
|
||||
mat-icon-button>
|
||||
<mat-icon *ngIf="selection.isSelected(item)" svgIcon="mdi_pin"></mat-icon>
|
||||
<mat-icon svgIcon="mdi_pin_outline" *ngIf="!selection.isSelected(item)"></mat-icon>
|
||||
</button>
|
||||
|
||||
<template [ngTemplateOutlet]="deleteButton" [ngTemplateOutletContext]="{key: item}"></template>
|
||||
<template [ngTemplateOutlet]="toggleButton" [ngTemplateOutletContext]="{key: item}"></template>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -52,15 +50,9 @@
|
||||
}}</span>
|
||||
<span class="fill-space"></span>
|
||||
</div>
|
||||
<button *ngIf="item.projectId !== zitadelProjectId" matTooltip="{{'ACTIONS.DELETE' | translate}}" color="warn"
|
||||
(click)="deleteProject(item)" class="delete-button" mat-icon-button>
|
||||
<i class="las la-trash"></i>
|
||||
</button>
|
||||
<button matTooltip="{{'ACTIONS.PIN' | translate}}" [ngClass]="{ selected: selection.isSelected(item)}"
|
||||
(click)="selection.toggle(item)" class="edit-button" mat-icon-button>
|
||||
<mat-icon *ngIf="selection.isSelected(item)" svgIcon="mdi_pin"></mat-icon>
|
||||
<mat-icon svgIcon="mdi_pin_outline" *ngIf="!selection.isSelected(item)"></mat-icon>
|
||||
</button>
|
||||
|
||||
<template [ngTemplateOutlet]="deleteButton" [ngTemplateOutletContext]="{key: item}"></template>
|
||||
<template [ngTemplateOutlet]="toggleButton" [ngTemplateOutletContext]="{key: item}"></template>
|
||||
</div>
|
||||
|
||||
<p class="n-items" *ngIf="!loading && items.length === 0">{{'PROJECT.PAGES.NOITEMS' | translate}}</p>
|
||||
@@ -71,4 +63,19 @@
|
||||
<span>{{'PROJECT.PAGES.ADDNEW' | translate}}</span>
|
||||
</div>
|
||||
</ng-template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ng-template #deleteButton let-key="key">
|
||||
<button *ngIf="key.projectId !== zitadelProjectId" matTooltip="{{'ACTIONS.DELETE' | translate}}" color="warn"
|
||||
(click)="deleteProject($event, key)" class="delete-button" mat-icon-button>
|
||||
<i class="las la-trash"></i>
|
||||
</button>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #toggleButton let-key="key">
|
||||
<button matTooltip="{{'ACTIONS.PIN' | translate}}" [ngClass]="{ selected: selection.isSelected(key)}"
|
||||
(click)="toggle(key,$event)" class="edit-button" mat-icon-button>
|
||||
<mat-icon *ngIf="selection.isSelected(key)" svgIcon="mdi_pin"></mat-icon>
|
||||
<mat-icon svgIcon="mdi_pin_outline" *ngIf="!selection.isSelected(key)"></mat-icon>
|
||||
</button>
|
||||
</ng-template>
|
||||
+20
-12
@@ -63,11 +63,10 @@ export class OwnedProjectGridComponent implements OnChanges {
|
||||
this.setPrefixedItem('pinned-projects', JSON.stringify(
|
||||
this.selection.selected.map(item => item.projectId),
|
||||
)).then(() => {
|
||||
const filtered = this.notPinned.filter(item => item === selection.added.find(i => i === item));
|
||||
filtered.forEach((f, i) => {
|
||||
this.notPinned.splice(i, 1);
|
||||
selection.added.forEach(item => {
|
||||
const index = this.notPinned.findIndex(i => i.projectId === item.projectId);
|
||||
this.notPinned.splice(index, 1);
|
||||
});
|
||||
|
||||
this.notPinned.push(...selection.removed);
|
||||
});
|
||||
});
|
||||
@@ -102,13 +101,6 @@ export class OwnedProjectGridComponent implements OnChanges {
|
||||
}
|
||||
});
|
||||
this.selection.select(...toSelect);
|
||||
|
||||
const toNotPinned: ProjectView.AsObject[] = this.items.filter((item, index) => {
|
||||
if (!array.includes(item.projectId)) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
this.notPinned = toNotPinned;
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -133,7 +125,13 @@ export class OwnedProjectGridComponent implements OnChanges {
|
||||
this.changedView.emit(true);
|
||||
}
|
||||
|
||||
public deleteProject(item: ProjectView.AsObject): void {
|
||||
public toggle(item: ProjectView.AsObject, event: any): void {
|
||||
event.stopPropagation();
|
||||
this.selection.toggle(item);
|
||||
}
|
||||
|
||||
public deleteProject(event: any, item: ProjectView.AsObject): void {
|
||||
event.stopPropagation();
|
||||
const dialogRef = this.dialog.open(WarnDialogComponent, {
|
||||
data: {
|
||||
confirmKey: 'ACTIONS.DELETE',
|
||||
@@ -152,6 +150,16 @@ export class OwnedProjectGridComponent implements OnChanges {
|
||||
if (index > -1) {
|
||||
this.items.splice(index, 1);
|
||||
}
|
||||
|
||||
const indexSelection = this.selection.selected.findIndex(iter => iter.projectId === item.projectId);
|
||||
if (indexSelection > -1) {
|
||||
this.selection.selected.splice(indexSelection, 1);
|
||||
}
|
||||
|
||||
const indexPinned = this.notPinned.findIndex(iter => iter.projectId === item.projectId);
|
||||
if (indexPinned > -1) {
|
||||
this.notPinned.splice(indexPinned, 1);
|
||||
}
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
|
||||
+13
-3
@@ -4,10 +4,11 @@ import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { MatPaginator, PageEvent } from '@angular/material/paginator';
|
||||
import { MatTableDataSource } from '@angular/material/table';
|
||||
import { Router } from '@angular/router';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb';
|
||||
import { BehaviorSubject, Observable, Subscription } from 'rxjs';
|
||||
import { take } from 'rxjs/operators';
|
||||
import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component';
|
||||
import { ProjectView } from 'src/app/proto/generated/management_pb';
|
||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
@@ -59,6 +60,7 @@ export class OwnedProjectListComponent implements OnInit, OnDestroy {
|
||||
public zitadelProjectId: string = '';
|
||||
|
||||
constructor(private router: Router,
|
||||
private route: ActivatedRoute,
|
||||
public translate: TranslateService,
|
||||
private mgmtService: ManagementService,
|
||||
private toast: ToastService,
|
||||
@@ -70,7 +72,15 @@ export class OwnedProjectListComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
public ngOnInit(): void {
|
||||
this.getData(10, 0);
|
||||
this.route.queryParams.pipe(take(1)).subscribe(params => {
|
||||
console.log(params);
|
||||
this.getData();
|
||||
if (params.deferredReload) {
|
||||
setTimeout(() => {
|
||||
this.getData();
|
||||
}, 2000);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public ngOnDestroy(): void {
|
||||
@@ -97,7 +107,7 @@ export class OwnedProjectListComponent implements OnInit, OnDestroy {
|
||||
this.router.navigate(['/projects', 'create']);
|
||||
}
|
||||
|
||||
private async getData(limit: number, offset: number): Promise<void> {
|
||||
private async getData(limit?: number, offset?: number): Promise<void> {
|
||||
this.loadingSubject.next(true);
|
||||
this.mgmtService.SearchProjects(limit, offset).then(res => {
|
||||
const response = res.toObject();
|
||||
|
||||
Reference in New Issue
Block a user