feat: rehauled console (#3525)

* new console

* move npm ci to angular build

* rel path for assets

* local grpc copy

* login policy, rm clear views, features rel path

* lock

Co-authored-by: Livio Amstutz <[email protected]>
This commit is contained in:
Max Peintner
2022-04-28 10:35:02 +00:00
committed by GitHub
co-authored by Livio Amstutz
parent 00f7dbe875
commit 08ae39ae19
668 changed files with 47746 additions and 19117 deletions
@@ -115,26 +115,27 @@ export class UserGrantsDataSource extends DataSource<UserGrant.AsObject> {
}
private loadResponse(promise: Promise<ListUserGrantResponse.AsObject>): void {
from(promise).pipe(
map(resp => {
if (resp.details?.totalResult) {
this.totalResult = resp.details.totalResult;
} else {
this.totalResult = 0;
}
if (resp.details?.viewTimestamp) {
this.viewTimestamp = resp.details.viewTimestamp;
}
return resp.resultList;
}),
catchError(() => of([])),
finalize(() => this.loadingSubject.next(false)),
).subscribe(grants => {
this.grantsSubject.next(grants);
});
from(promise)
.pipe(
map((resp) => {
if (resp.details?.totalResult) {
this.totalResult = resp.details.totalResult;
} else {
this.totalResult = 0;
}
if (resp.details?.viewTimestamp) {
this.viewTimestamp = resp.details.viewTimestamp;
}
return resp.resultList;
}),
catchError(() => of([])),
finalize(() => this.loadingSubject.next(false)),
)
.subscribe((grants) => {
this.grantsSubject.next(grants);
});
}
/**
* Connect this data source to the table. The table will only update when
* the returned stream emits new items.
@@ -1,18 +1,31 @@
<cnsl-refresh-table [loading]="dataSource?.loading$ | async" (refreshed)="changePage()"
<cnsl-refresh-table [loading]="dataSource?.loading$ | async" (refreshed)="changePage()" [hideRefresh]="true"
[emitRefreshOnPreviousRoutes]="refreshOnPreviousRoutes" [timestamp]="dataSource?.viewTimestamp"
[dataSize]="dataSource?.totalResult ?? 0" [selection]="selection">
<cnsl-form-field @appearfade *ngIf="userGrantListSearchKey !== undefined" actions class="filtername">
<input class="filterinput" cnslInput (keyup)="applyFilter($event)"
[placeholder]="('USER.GRANTS.FILTER.' + userGrantListSearchKey.toString()) | translate" #input>
</cnsl-form-field>
<!-- <div leftActions class="user-grants-table-left-actions">
<button class="user-grant-type-button" [ngClass]="{'active': type === undefined}"
(click)="setType(undefined)">{{'PROJECT.GRANT.ALL' | translate}}</button>
<button class="user-grant-type-button" [ngClass]="{'active': type === Type.TYPE_HUMAN}"
(click)="setType(Type.TYPE_HUMAN)">{{'USER.TABLE.TYPES.HUMAN' | translate}}</button>
<button class="user-grant-type-button" [ngClass]="{'active': type === Type.TYPE_MACHINE}"
(click)="setType(Type.TYPE_MACHINE)">{{'USER.TABLE.TYPES.MACHINE' | translate}}</button>
</div> -->
<button color="warn" matTooltip="{{'GRANTS.DELETE' | translate}}" class="icon-button" mat-icon-button actions
<button color="warn" matTooltip="{{'GRANTS.DELETE' | translate}}" class="cnsl-action-button" mat-raised-button actions
(click)="deleteGrantSelection()" *ngIf="selection.hasValue() && disableDelete === false">
<i class="las la-trash"></i>
<span>{{'ACTIONS.DELETE' | translate}}</span>
<cnsl-action-keys (actionTriggered)="deleteGrantSelection()" [type]="ActionKeysType.DELETE"></cnsl-action-keys>
</button>
<a *ngIf="disableWrite === false" matTooltip="{{'GRANTS.ADD' | translate}}" actions color="primary" color="primary"
mat-raised-button [routerLink]="routerLink">
<mat-icon class="icon">add</mat-icon>{{ 'GRANTS.ADD_BTN' | translate }}
<cnsl-filter-user-grants actions *ngIf="!selection.hasValue()" (filterChanged)="applySearchQuery($any($event))"
(filterOpen)="filterOpen = $event"></cnsl-filter-user-grants>
<a actions *ngIf="disableWrite === false && (selection.hasValue() === false)"
matTooltip="{{'GRANTS.ADD' | translate}}" color="primary" class="cnsl-action-button" mat-raised-button
[routerLink]="routerLink">
<mat-icon class="icon">add</mat-icon>
<span>{{ 'GRANTS.ADD_BTN' | translate }}</span>
<cnsl-action-keys (actionTriggered)="gotoCreateLink(routerLink)" [type]="ActionKeysType.ADD"></cnsl-action-keys>
</a>
<div class="table-wrapper">
@@ -29,154 +42,103 @@
[disabled]="disableWrite || !((['user.grant.write$'] | hasRole | async) || ((context === UserGrantContext.OWNED_PROJECT ? ['user.grant.write:' + row?.projectId] : context === UserGrantContext.GRANTED_PROJECT ? ['user.grant.write:' + row?.id] : []) | hasRole | async))"
color="primary" (click)="$event.stopPropagation()" (change)="$event ? selection.toggle(row) : null"
[checked]="selection.isSelected(row)">
<cnsl-avatar
*ngIf="context !== UserGrantContext.USER && row && row?.displayName && row.firstName && row.lastName"
class="avatar" [name]="row.displayName" [avatarUrl]="row.avatarUrl || ''"
[forColor]="row?.preferredLoginName" [size]="32">
<cnsl-avatar *ngIf="row && row?.displayName && row.firstName && row.lastName; else cog" class="avatar"
[name]="row.displayName" [avatarUrl]="row.avatarUrl || ''" [forColor]="row?.preferredLoginName"
[size]="32">
</cnsl-avatar>
<ng-template #cog>
<cnsl-avatar class="avatar" [isMachine]="true" [forColor]="row.preferredLoginName" [size]="32">
</cnsl-avatar>
</ng-template>
</mat-checkbox>
</td>
</ng-container>
<ng-container matColumnDef="user">
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.GRANT.USER' | translate }}
<template [ngTemplateOutlet]="templateRef"
[ngTemplateOutletContext]="{key: UserGrantListSearchKey.DISPLAY_NAME}"></template>
</th>
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.GRANT.USER' | translate }} </th>
<td mat-cell *matCellDef="let grant">
<a class="user" [routerLink]="['/users', grant.userId]">{{grant?.displayName}}</a>
<a class="user">{{grant.displayName ? grant.displayName :
grant.userName ? grant.userName : ''}}</a>
</td>
</ng-container>
<ng-container matColumnDef="org">
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.GRANT.GRANTEDORGDOMAIN' | translate }}
<template [ngTemplateOutlet]="templateRef"
[ngTemplateOutletContext]="{key: UserGrantListSearchKey.ORG_NAME}"></template>
</th>
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.GRANT.ORG' | translate }} </th>
<td mat-cell *matCellDef="let grant">
{{grant.orgName}} </td>
</ng-container>
<ng-container matColumnDef="projectId">
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.GRANT.PROJECTNAME' | translate }}
<template [ngTemplateOutlet]="templateRef"
[ngTemplateOutletContext]="{key: UserGrantListSearchKey.PROJECT_NAME}"></template>
</th>
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.GRANT.PROJECTNAME' | translate }} </th>
<td mat-cell *matCellDef="let grant">
{{grant.projectName}} </td>
</ng-container>
<ng-container matColumnDef="dates">
<th mat-header-cell *matHeaderCellDef> DATES </th>
<ng-container matColumnDef="type">
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.GRANT.TYPE' | translate }} </th>
<td mat-cell *matCellDef="let grant">
<div class="date-block" *ngIf="grant.details?.creationDate">
<span class="date-sub">{{ 'PROJECT.GRANT.CREATIONDATE' | translate }}:</span>
<span>{{grant.details.creationDate | timestampToDate | localizedDate: 'dd. MMM, HH:mm' }}</span>
</div>
<div class="date-block" *ngIf="grant.details?.changeDate">
<span class="date-sub">{{ 'PROJECT.GRANT.CHANGEDATE' | translate }}</span>
<span>{{grant.details.changeDate | timestampToDate | localizedDate: 'dd. MMM, HH:mm' }}</span>
</div>
<span class="state">{{getType(grant)}}</span>
</td>
</ng-container>
<ng-container matColumnDef="creationDate">
<th mat-header-cell *matHeaderCellDef> {{'PROJECT.GRANT.CREATIONDATE' | translate}} </th>
<td mat-cell *matCellDef="let grant">
<span>{{grant.details.creationDate | timestampToDate | localizedDate: 'dd. MMM, HH:mm' }}</span>
<span class="no-break">{{grant.details.creationDate | timestampToDate | localizedDate: 'dd. MMM, HH:mm'
}}</span>
</td>
</ng-container>
<ng-container matColumnDef="changeDate">
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.GRANT.CHANGEDATE' | translate }} </th>
<td mat-cell *matCellDef="let grant">
<span>{{grant.details.changeDate | timestampToDate | localizedDate: 'dd. MMM, HH:mm' }}</span>
<span class="no-break">{{grant.details.changeDate | timestampToDate | localizedDate: 'dd. MMM, HH:mm'
}}</span>
</td>
</ng-container>
<ng-container matColumnDef="roleNamesList">
<th mat-header-cell *matHeaderCellDef class="role-data">
{{ 'PROJECT.GRANT.ROLENAMESLIST' | translate }}
<template [ngTemplateOutlet]="templateRef"
[ngTemplateOutletContext]="{key: UserGrantListSearchKey.ROLE_KEY}"></template>
</th>
<td mat-cell *matCellDef="let grant; let i = index" class="role-data">
<ng-container
*ngIf="(context === UserGrantContext.USER || context === UserGrantContext.NONE) && (grant.id && grantToEdit !== grant.id) || (grantToEdit !== grant.id)">
<div class="flex-row">
<div class="role">
<span *ngFor="let role of grant.roleKeysList">{{ role }}</span>
</div>
<span class="fill-space"></span>
<button mat-stroked-button *ngIf="grant.id ? grantToEdit !== grant.id : grantToEdit !== grant.id"
[disabled]="disableWrite || !((['user.grant.write$'] | hasRole | async) || ((context === UserGrantContext.OWNED_PROJECT ? ['user.grant.write:' + grant?.projectId] : context === UserGrantContext.GRANTED_PROJECT ? ['user.grant.write:' + grant?.id] : []) | hasRole | async))"
(click)="loadGrantOptions(grant)" matTooltip="{{'ACTIONS.CHANGE' | translate}}">
<i class="las la-edit"></i>
{{'ACTIONS.EDIT' | translate}}
</button>
</div>
</ng-container>
<div class="row-form">
<ng-container
*ngIf="(context === UserGrantContext.OWNED_PROJECT || context === UserGrantContext.USER || context === UserGrantContext.NONE) && grantToEdit === grant.id && loadedProjectId && loadedProjectId === grant.projectId">
<cnsl-form-field class="form-field" appearance="outline">
<mat-select [(ngModel)]="grant.roleKeysList" multiple
[disabled]="disableWrite || !((['user.grant.write$'] | hasRole | async) || ((context === UserGrantContext.OWNED_PROJECT ? ['user.grant.write:' + grant?.projectId] : context === UserGrantContext.GRANTED_PROJECT ? ['user.grant.write:' + grant?.id] : []) | hasRole | async))"
(selectionChange)="updateRoles(grant, $event)">
<mat-option *ngFor="let role of projectRoleOptions" [value]="role.key">
{{role.key}}
</mat-option>
</mat-select>
</cnsl-form-field>
<button matTooltip="{{'ACTIONS.CLOSE' | translate}}"
*ngIf="context === UserGrantContext.USER || context === UserGrantContext.NONE" mat-icon-button
(click)="grantToEdit=''">
<mat-icon>close</mat-icon>
</button>
</ng-container>
<ng-container
*ngIf="(context === UserGrantContext.GRANTED_PROJECT || context === UserGrantContext.USER || context === UserGrantContext.NONE) && loadedId && loadedId === grant.projectId && grantToEdit === grant.id">
<cnsl-form-field class="form-field" appearance="outline">
<mat-select [(ngModel)]="grant.roleKeysList" multiple
[disabled]="disableWrite || !((['user.grant.write$'] | hasRole | async) || ((context === UserGrantContext.OWNED_PROJECT ? ['user.grant.write:' + grant?.projectId] : context === UserGrantContext.GRANTED_PROJECT ? ['user.grant.write:' + grant?.id] : []) | hasRole | async))"
(selectionChange)="updateRoles(grant, $event)">
<mat-option *ngFor="let role of grantRoleOptions" [value]="role">
{{role}}
</mat-option>
</mat-select>
</cnsl-form-field>
<button matTooltip="{{'ACTIONS.CLOSE' | translate}}"
*ngIf="context === UserGrantContext.USER || context === UserGrantContext.NONE" mat-icon-button
(click)="grantToEdit=''">
<mat-icon>close</mat-icon>
</button>
</ng-container>
<div class="flex-row">
<cnsl-project-role-chip [roleName]="role" *ngFor="let role of grant.roleKeysList">{{ role }}
</cnsl-project-role-chip>
</div>
</td>
</ng-container>
<ng-container matColumnDef="actions" stickyEnd>
<th mat-header-cell *matHeaderCellDef class="user-tr-actions"> </th>
<td mat-cell class="user-tr-actions" *matCellDef="let grant; let i = index">
<cnsl-table-actions [hasActions]="true">
<button actions matTooltip="{{'ACTIONS.REMOVE' | translate}}" color="warn" (click)="deleteGrant(grant)"
mat-icon-button>
<i class="las la-trash"></i>
</button>
<button menuActions mat-menu-item [routerLink]="['/users', grant.userId]">
{{'ACTIONS.TABLE.SHOWUSER' | translate : ({value: grant.displayName})}}
</button>
</cnsl-table-actions>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;">
<tr (click)="openEditDialog(grant)" class="highlight pointer" mat-row
*matRowDef="let grant; columns: displayedColumns;">
</tr>
</table>
<div *ngIf="(dataSource.loading$ | async) === false && !dataSource?.totalResult" class="no-content-row">
<i class="las la-exclamation"></i>
<span>{{'GRANTS.EMPTY' | translate}}</span>
</div>
<cnsl-paginator class="paginator" #paginator [timestamp]="dataSource?.viewTimestamp"
[length]="dataSource.totalResult" [pageSize]="INITIAL_PAGE_SIZE" [length]="dataSource.totalResult"
[pageSizeOptions]="[2, 3, 25, 50, 100, 250]" (page)="changePage($event)">
</cnsl-paginator>
</div>
</cnsl-refresh-table>
<div *ngIf="(dataSource.loading$ | async) === false && !dataSource?.totalResult" class="no-content-row">
<i class="las la-exclamation"></i>
<span>{{'GRANTS.EMPTY' | translate}}</span>
</div>
<cnsl-paginator class="paginator" #paginator [timestamp]="dataSource?.viewTimestamp" [length]="dataSource.totalResult"
[pageSize]="INITIAL_PAGE_SIZE" [length]="dataSource.totalResult" [pageSizeOptions]="[2, 3, 25, 50, 100, 250]"
(page)="changePage($event)">
</cnsl-paginator>
<ng-template #templateRef let-key="key">
<button class="search-button" mat-icon-button (click)="setFilter(key)">
<mat-icon class="icon" *ngIf="this.userGrantListSearchKey !== key">search</mat-icon>
<mat-icon class="icon" *ngIf="this.userGrantListSearchKey === key">search_off</mat-icon>
</button>
</ng-template>
</cnsl-refresh-table>
@@ -1,96 +1,73 @@
.table-wrapper {
overflow: auto;
@use '@angular/material' as mat;
.table,
.paginator {
width: 100%;
background-color: #2d2e30;
@mixin user-grants-theme($theme) {
$foreground: map-get($theme, foreground);
$is-dark-theme: map-get($theme, is-dark);
td,
th {
padding-left: .5rem;
padding-right: .5rem;
.user-grants-table-left-actions {
display: flex;
align-items: center;
&:first-child {
padding-left: 0;
padding-right: .5rem;
.user-grant-type-button {
border: none;
background: none;
text-align: left;
padding: 0.75rem 0;
opacity: 0.6;
font-size: 15px;
cursor: pointer;
color: map-get($foreground, base);
&:not(:last-child) {
margin-right: 1rem;
}
&:last-child {
padding-right: 0;
}
}
.user {
text-decoration: none;
color: inherit;
}
th {
.search-button {
visibility: hidden;
width: 30px;
.icon {
font-size: 1.2rem;
}
&:hover {
opacity: 1;
}
&:hover,
&.search-active {
.search-button {
visibility: visible;
}
&.active {
font-weight: 600;
opacity: 1;
}
}
}
}
.no-roles {
font-size: 13px;
color: var(--grey);
.no-break {
white-space: nowrap;
}
.user {
text-decoration: none;
color: inherit;
}
th {
.search-button {
visibility: hidden;
width: 30px;
.icon {
font-size: 1.2rem;
}
}
&:hover,
&.search-active {
.search-button {
visibility: visible;
}
}
}
.flex-row {
display: flex;
flex-direction: row;
max-width: 400px;
.role {
display: flex;
flex-direction: column;
margin: .25rem;
font-size: 14px;
text-overflow: ellipsis;
overflow: hidden;
justify-items: center;
align-self: center;
}
.fill-space {
flex: 1;
}
button {
margin: .5rem 0;
display: block;
max-width: 120px;
box-sizing: border-box;
i {
font-size: 1.2rem;
margin-bottom: 5px;
}
}
}
.row-form {
display: flex;
align-items: center;
.form-field {
flex: 1;
}
flex-wrap: wrap;
padding: 3px 0;
}
.fill-space {
@@ -101,14 +78,17 @@
min-width: 180px;
}
.user-grants-tr-actions {
width: 50px;
}
.date-block {
margin: .5rem 0;
margin: 0.5rem 0;
display: block;
min-width: 120px;
.date-sub {
font-size: 13px;
color: var(--grey);
display: block;
}
}
@@ -1,24 +1,21 @@
import { SelectionModel } from '@angular/cdk/collections';
import { AfterViewInit, Component, Input, OnInit, ViewChild } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { MatInput } from '@angular/material/input';
import { MatSelectChange } from '@angular/material/select';
import { MatTable } from '@angular/material/table';
import { Router } from '@angular/router';
import { tap } from 'rxjs/operators';
import { enterAnimations } from 'src/app/animations';
import { TextQueryMethod } from 'src/app/proto/generated/zitadel/object_pb';
import { Role } from 'src/app/proto/generated/zitadel/project_pb';
import {
UserGrant,
UserGrantDisplayNameQuery,
UserGrantOrgNameQuery,
UserGrantProjectNameQuery,
UserGrantQuery,
UserGrantRoleKeyQuery,
} from 'src/app/proto/generated/zitadel/user_pb';
import { Type, UserGrant, UserGrantQuery } from 'src/app/proto/generated/zitadel/user_pb';
import { ManagementService } from 'src/app/services/mgmt.service';
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 { WarnDialogComponent } from '../warn-dialog/warn-dialog.component';
import { UserGrantContext, UserGrantsDataSource } from './user-grants-datasource';
export enum UserGrantListSearchKey {
@@ -31,9 +28,7 @@ export enum UserGrantListSearchKey {
selector: 'cnsl-user-grants',
templateUrl: './user-grants.component.html',
styleUrls: ['./user-grants.component.scss'],
animations: [
enterAnimations,
],
animations: [enterAnimations],
})
export class UserGrantsComponent implements OnInit, AfterViewInit {
public userGrantListSearchKey: UserGrantListSearchKey | undefined = undefined;
@@ -56,7 +51,6 @@ export class UserGrantsComponent implements OnInit, AfterViewInit {
@Input() grantId: string = '';
@ViewChild('input') public filter!: MatInput;
public grantRoleOptions: string[] = [];
public projectRoleOptions: Role.AsObject[] = [];
public routerLink: any = [''];
@@ -65,17 +59,30 @@ export class UserGrantsComponent implements OnInit, AfterViewInit {
public grantToEdit: string = '';
public UserGrantContext: any = UserGrantContext;
public Type: any = Type;
public ActionKeysType: any = ActionKeysType;
@Input() public type: Type | undefined = undefined;
public filterOpen: boolean = false;
constructor(
private userService: ManagementService,
private mgmtService: ManagementService,
private toast: ToastService,
) { }
private dialog: MatDialog,
private router: Router,
) {}
@Input() public displayedColumns: string[] = ['select',
@Input() public displayedColumns: string[] = [
'select',
'user',
'org',
'projectId', 'creationDate', 'changeDate', 'roleNamesList'];
'projectId',
'type',
'creationDate',
'changeDate',
'roleNamesList',
'actions',
];
public ngOnInit(): void {
this.dataSource = new UserGrantsDataSource(this.userService);
@@ -83,14 +90,14 @@ export class UserGrantsComponent implements OnInit, AfterViewInit {
switch (this.context) {
case UserGrantContext.OWNED_PROJECT:
if (this.projectId) {
this.getProjectRoleOptions(this.projectId);
// this.getProjectRoleOptions(this.projectId);
this.routerLink = ['/grant-create', 'project', this.projectId];
}
break;
case UserGrantContext.GRANTED_PROJECT:
if (this.grantId) {
this.routerLink = ['/grant-create', 'project', this.projectId, 'grant', this.grantId];
this.getGrantRoleOptions(this.grantId, this.projectId);
// this.getGrantRoleOptions(this.grantId, this.projectId);
}
break;
case UserGrantContext.USER:
@@ -102,50 +109,34 @@ export class UserGrantsComponent implements OnInit, AfterViewInit {
this.routerLink = ['/grant-create'];
}
this.loadGrantsPage();
this.loadGrantsPage(this.type);
}
public ngAfterViewInit(): void {
this.paginator.page
.pipe(
tap(() => this.loadGrantsPage()),
)
.subscribe();
this.paginator.page.pipe(tap(() => this.loadGrantsPage(this.type))).subscribe();
}
private loadGrantsPage(filterValue?: string): void {
let queries: UserGrantQuery[] = [];
if (this.userGrantListSearchKey !== undefined && filterValue) {
const query = new UserGrantQuery();
switch (this.userGrantListSearchKey) {
case UserGrantListSearchKey.DISPLAY_NAME:
const ugDnQ = new UserGrantDisplayNameQuery();
ugDnQ.setDisplayName(filterValue);
ugDnQ.setMethod(TextQueryMethod.TEXT_QUERY_METHOD_CONTAINS_IGNORE_CASE);
query.setDisplayNameQuery(ugDnQ);
break;
case UserGrantListSearchKey.ORG_NAME:
const ugOnQ = new UserGrantOrgNameQuery();
ugOnQ.setOrgName(filterValue);
ugOnQ.setMethod(TextQueryMethod.TEXT_QUERY_METHOD_CONTAINS_IGNORE_CASE);
query.setOrgNameQuery(ugOnQ);
break;
case UserGrantListSearchKey.PROJECT_NAME:
const ugPnQ = new UserGrantProjectNameQuery();
ugPnQ.setProjectName(filterValue);
ugPnQ.setMethod(TextQueryMethod.TEXT_QUERY_METHOD_CONTAINS_IGNORE_CASE);
query.setProjectNameQuery(ugPnQ);
break;
case UserGrantListSearchKey.ROLE_KEY:
const ugRkQ = new UserGrantRoleKeyQuery();
ugRkQ.setRoleKey(filterValue);
ugRkQ.setMethod(TextQueryMethod.TEXT_QUERY_METHOD_CONTAINS_IGNORE_CASE);
query.setRoleKeyQuery(ugRkQ);
break;
public setType(type: Type | undefined): void {
this.type = type;
this.loadGrantsPage(type);
}
}
queries = [query];
public getType(grant: UserGrant.AsObject): string {
if (grant.projectGrantId) {
return 'Project Grant';
} else if (grant.projectId) {
return 'Project';
} else {
return '';
}
}
public gotoCreateLink(rL: any): void {
this.router.navigate(rL);
}
private loadGrantsPage(type: Type | undefined, searchQueries?: UserGrantQuery[]): void {
let queries: UserGrantQuery[] = [];
this.dataSource.loadGrants(
this.context,
@@ -156,7 +147,7 @@ export class UserGrantsComponent implements OnInit, AfterViewInit {
grantId: this.grantId,
userId: this.userId,
},
queries,
searchQueries ? [...searchQueries, ...queries] : queries,
);
}
@@ -167,62 +158,111 @@ export class UserGrantsComponent implements OnInit, AfterViewInit {
}
public masterToggle(): void {
this.isAllSelected() ?
this.selection.clear() :
this.dataSource.grantsSubject.value.forEach(row => this.selection.select(row));
this.isAllSelected()
? this.selection.clear()
: this.dataSource.grantsSubject.value.forEach((row) => this.selection.select(row));
}
public loadGrantOptions(grant: UserGrant.AsObject): void {
this.grantToEdit = grant.id;
if (grant.projectGrantId && grant.projectId) {
this.getGrantRoleOptions(grant.projectGrantId, grant.projectId);
} else if (grant.projectId) {
this.getProjectRoleOptions(grant.projectId);
}
}
private getGrantRoleOptions(id: string, projectId: string): void {
this.mgmtService.getGrantedProjectByID(projectId, id).then(resp => {
if (resp.grantedProject) {
this.loadedId = projectId;
this.grantRoleOptions = resp.grantedProject?.grantedRoleKeysList;
}
}).catch(error => {
this.grantToEdit = '';
this.toast.showError(error);
public openEditDialog(grant: UserGrant.AsObject): void {
const dialogRef = this.dialog.open(UserGrantRoleDialogComponent, {
data: {
projectId: grant.projectId,
grantId: grant.projectGrantId,
selectedRoleKeysList: grant.roleKeysList,
i18nTitle: 'GRANTS.EDIT.TITLE',
},
width: '600px',
});
}
private getProjectRoleOptions(projectId: string): void {
this.mgmtService.listProjectRoles(projectId, 100, 0).then(resp => {
this.loadedProjectId = projectId;
this.projectRoleOptions = resp.resultList;
dialogRef.afterClosed().subscribe((resp) => {
if (resp && resp.roles) {
this.userService
.updateUserGrant(grant.id, grant.userId, resp.roles)
.then(() => {
this.toast.showInfo('GRANTS.TOAST.UPDATED', true);
grant.roleKeysList = resp.roles;
})
.catch((error) => {
this.toast.showError(error);
});
}
});
}
updateRoles(grant: UserGrant.AsObject, selectionChange: MatSelectChange): void {
this.userService.updateUserGrant(grant.id, grant.userId, selectionChange.value)
this.userService
.updateUserGrant(grant.id, grant.userId, selectionChange.value)
.then(() => {
this.toast.showInfo('GRANTS.TOAST.UPDATED', true);
}).catch(error => {
})
.catch((error) => {
this.toast.showError(error);
});
}
deleteGrantSelection(): void {
this.userService.bulkRemoveUserGrant(this.selection.selected.map(grant => grant.id)).then(() => {
this.toast.showInfo('GRANTS.TOAST.BULKREMOVED', true);
const data = this.dataSource.grantsSubject.getValue();
this.selection.selected.forEach((item) => {
const index = data.findIndex(i => i.id === item.id);
if (index > -1) {
data.splice(index, 1);
this.dataSource.grantsSubject.next(data);
}
});
this.selection.clear();
}).catch(error => {
this.toast.showError(error);
public deleteGrant(grant: UserGrant.AsObject): void {
const dialogRef = this.dialog.open(WarnDialogComponent, {
data: {
confirmKey: 'ACTIONS.DELETE',
cancelKey: 'ACTIONS.CANCEL',
titleKey: 'GRANTS.DIALOG.DELETE_TITLE',
descriptionKey: 'GRANTS.DIALOG.DELETE_DESCRIPTION',
},
width: '400px',
});
dialogRef.afterClosed().subscribe((resp) => {
if (resp) {
this.userService
.removeUserGrant(grant.id, grant.userId)
.then(() => {
this.toast.showInfo('GRANTS.TOAST.REMOVED', true);
const data = this.dataSource.grantsSubject.getValue();
const index = data.findIndex((i) => i.id === grant.id);
if (index > -1) {
data.splice(index, 1);
this.dataSource.grantsSubject.next(data);
}
})
.catch((error) => {
this.toast.showError(error);
});
}
});
}
public deleteGrantSelection(): void {
const dialogRef = this.dialog.open(WarnDialogComponent, {
data: {
confirmKey: 'ACTIONS.DELETE',
cancelKey: 'ACTIONS.CANCEL',
titleKey: 'GRANTS.DIALOG.BULK_DELETE_TITLE',
descriptionKey: 'GRANTS.DIALOG.BULK_DELETE_DESCRIPTION',
},
width: '400px',
});
dialogRef.afterClosed().subscribe((resp) => {
if (resp) {
this.userService
.bulkRemoveUserGrant(this.selection.selected.map((grant) => grant.id))
.then(() => {
this.toast.showInfo('GRANTS.TOAST.BULKREMOVED', true);
const data = this.dataSource.grantsSubject.getValue();
this.selection.selected.forEach((item) => {
const index = data.findIndex((i) => i.id === item.id);
if (index > -1) {
data.splice(index, 1);
this.dataSource.grantsSubject.next(data);
}
});
this.selection.clear();
})
.catch((error) => {
this.toast.showError(error);
});
}
});
}
@@ -239,11 +279,9 @@ export class UserGrantsComponent implements OnInit, AfterViewInit {
);
}
public applyFilter(event: Event): void {
public applySearchQuery(searchQueries?: UserGrantQuery[]): void {
this.selection.clear();
const filterValue = (event.target as HTMLInputElement).value;
this.loadGrantsPage(filterValue);
this.loadGrantsPage(this.type, searchQueries);
}
public setFilter(key: UserGrantListSearchKey): void {
@@ -257,7 +295,7 @@ export class UserGrantsComponent implements OnInit, AfterViewInit {
this.userGrantListSearchKey = key;
} else {
this.userGrantListSearchKey = undefined;
this.loadGrantsPage();
this.loadGrantsPage(this.type);
}
}
}
@@ -16,37 +16,46 @@ import { LocalizedDatePipeModule } from 'src/app/pipes/localized-date-pipe/local
import { TimestampToDatePipeModule } from 'src/app/pipes/timestamp-to-date-pipe/timestamp-to-date-pipe.module';
import { InputModule } from '../../modules/input/input.module';
import { ActionKeysModule } from '../action-keys/action-keys.module';
import { AvatarModule } from '../avatar/avatar.module';
import { FilterUserGrantsModule } from '../filter-user-grants/filter-user-grants.module';
import { PaginatorModule } from '../paginator/paginator.module';
import { ProjectRoleChipModule } from '../project-role-chip/project-role-chip.module';
import { RefreshTableModule } from '../refresh-table/refresh-table.module';
import { TableActionsModule } from '../table-actions/table-actions.module';
import { UserGrantRoleDialogModule } from '../user-grant-role-dialog/user-grant-role-dialog.module';
import { WarnDialogModule } from '../warn-dialog/warn-dialog.module';
import { UserGrantsComponent } from './user-grants.component';
@NgModule({
declarations: [UserGrantsComponent],
imports: [
CommonModule,
FormsModule,
AvatarModule,
MatButtonModule,
HasRoleModule,
MatTableModule,
PaginatorModule,
MatIconModule,
RouterModule,
MatProgressSpinnerModule,
MatCheckboxModule,
MatTooltipModule,
MatSelectModule,
TranslateModule,
HasRolePipeModule,
TimestampToDatePipeModule,
RefreshTableModule,
LocalizedDatePipeModule,
InputModule,
],
exports: [
UserGrantsComponent,
],
declarations: [UserGrantsComponent],
imports: [
CommonModule,
FormsModule,
AvatarModule,
MatButtonModule,
HasRoleModule,
MatTableModule,
PaginatorModule,
MatIconModule,
RouterModule,
ProjectRoleChipModule,
MatProgressSpinnerModule,
MatCheckboxModule,
MatTooltipModule,
TableActionsModule,
UserGrantRoleDialogModule,
MatSelectModule,
TranslateModule,
ActionKeysModule,
FilterUserGrantsModule,
HasRolePipeModule,
TimestampToDatePipeModule,
RefreshTableModule,
LocalizedDatePipeModule,
InputModule,
WarnDialogModule,
],
exports: [UserGrantsComponent],
})
export class UserGrantsModule { }
export class UserGrantsModule {}