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:
Max Peintner
2020-11-13 09:59:11 +01:00
committed by GitHub
co-authored by Fabi
parent 966e3850ed
commit 42effd8702
49 changed files with 599 additions and 227 deletions
@@ -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>
@@ -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;
}
});
}
@@ -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);
});
@@ -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>
@@ -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);
});
@@ -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();