mirror of
https://github.com/andwati/zitadel.git
synced 2026-10-31 19:42:15 +00:00
fix(console): refresh tables, auto refresh emitter, avatar colors (#487)
* refreshtable component * project grant refresh table * project role refresh, user grant, i18n * lint * auth user mfa table * auth mfa table * rm unused 404 page, add mgmt mfa table * change light accent color * add actions to mfa table * user detail mfa table * clear selection on refresh, bind data length * member table * fix padding mfa table * Update console/src/assets/i18n/en.json Co-authored-by: Florian Forster <[email protected]> * Update console/src/assets/i18n/en.json Co-authored-by: Florian Forster <[email protected]> * z-index, new colors * new senf color Co-authored-by: Florian Forster <[email protected]>
This commit is contained in:
co-authored by
Florian Forster
parent
933193855a
commit
117a0d7b19
@@ -1,5 +1,5 @@
|
||||
<div class="avatar-circle dontcloseonclick"
|
||||
[ngStyle]="{'height': size+'px', 'width': size+'px', 'fontSize': fontSize+'px', 'background-color': color}"
|
||||
[ngStyle]="{'height': size+'px', 'width': size+'px', 'fontSize': (fontSize-1)+'px', 'background-color': color}"
|
||||
[ngClass]="{'active': active}">
|
||||
{{credentials}}
|
||||
</div>
|
||||
@@ -30,22 +30,22 @@ export class AvatarComponent implements OnInit {
|
||||
|
||||
getColor(userName: string): string {
|
||||
const colors = [
|
||||
'#e51c23',
|
||||
'#e91e63',
|
||||
'#9c27b0',
|
||||
'#673ab7',
|
||||
'#3f51b5',
|
||||
'#5677fc',
|
||||
'#03a9f4',
|
||||
'#00bcd4',
|
||||
'#009688',
|
||||
'#259b24',
|
||||
'#8bc34a',
|
||||
'#afb42b',
|
||||
'#ff9800',
|
||||
'#ff5722',
|
||||
'#795548',
|
||||
'#607d8b',
|
||||
'#B44D51',
|
||||
'#B75073',
|
||||
'#84498E',
|
||||
'#705998',
|
||||
'#5C6598',
|
||||
'#7F90D3',
|
||||
'#3E93B9',
|
||||
'#3494A0',
|
||||
'#25716A',
|
||||
'#427E41',
|
||||
'#89A568',
|
||||
'#90924D',
|
||||
'#E2B032',
|
||||
'#C97358',
|
||||
'#6D5B54',
|
||||
'#6B7980',
|
||||
];
|
||||
|
||||
let hash = 0;
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
<div class="people">
|
||||
<div class="img-list">
|
||||
<ng-container *ngIf="totalResult < 10; else compact">
|
||||
<ng-container *ngFor="let member of membersSubject | async">
|
||||
<div (click)="showDetail()" class="avatar-circle"
|
||||
<ng-container *ngFor="let member of membersSubject | async; index as i">
|
||||
<div (click)="showDetail()" class="avatar-circle" [ngStyle]="{'z-index': 100 - i}"
|
||||
matTooltip="{{ member.email }} | {{member.rolesList?.join(' ')}}">
|
||||
<app-avatar *ngIf="member && member.firstName && member.lastName; else thumbavatar"
|
||||
class="avatar dontcloseonclick" [name]="member.firstName + ' '+ member.lastName"
|
||||
|
||||
@@ -28,18 +28,18 @@
|
||||
margin-left: 1rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
transition: all .15s ease-in-out;
|
||||
|
||||
.avatar-circle {
|
||||
transition: all .3s ease-in-out;
|
||||
float: left;
|
||||
margin: 0 8px 0 -15px;
|
||||
height: 32px;
|
||||
width: 32px;
|
||||
border-radius: 50%;
|
||||
&:not(:first-child) {
|
||||
-webkit-box-shadow: -9px 0px 20px -6px rgba(0,0,0,0.75);
|
||||
-moz-box-shadow: -9px 0px 20px -6px rgba(0,0,0,0.75);
|
||||
box-shadow: -9px 0px 20px -6px rgba(0,0,0,0.75);
|
||||
}
|
||||
-webkit-box-shadow: 2px 0px 7px -1px rgba(33,34,36,.5);
|
||||
-moz-box-shadow: 2px 0px 7px -1px rgba(33,34,36,.5);
|
||||
box-shadow: 2px 0px 7px -1px rgba(33,34,36,.5);
|
||||
}
|
||||
|
||||
.add-img {
|
||||
|
||||
@@ -1,44 +1,32 @@
|
||||
<div class="max-width-container">
|
||||
<div class="container">
|
||||
<div class="left">
|
||||
<a *ngIf="project" [routerLink]="[ '/projects', project.projectId]" mat-icon-button>
|
||||
<mat-icon class="icon">arrow_back</mat-icon>
|
||||
</a>
|
||||
<div class="container">
|
||||
<div class="left">
|
||||
<a *ngIf="project" [routerLink]="[ '/projects', project.projectId]" mat-icon-button>
|
||||
<mat-icon class="icon">arrow_back</mat-icon>
|
||||
</a>
|
||||
</div>
|
||||
<div class="right">
|
||||
<div class="head">
|
||||
<h1>{{projectName}} {{ 'PROJECT.MEMBER.TITLE' | translate }}</h1>
|
||||
<p class="desc">{{ 'PROJECT.MEMBER.DESCRIPTION' | translate }}</p>
|
||||
</div>
|
||||
<div class="right">
|
||||
<div class="head">
|
||||
<h1>{{projectName}} {{ 'PROJECT.MEMBER.TITLE' | translate }}</h1>
|
||||
<p class="desc">{{ 'PROJECT.MEMBER.DESCRIPTION' | translate }}</p>
|
||||
</div>
|
||||
|
||||
<div class="table-header-row" *ngIf="project">
|
||||
<div class="col">
|
||||
<ng-container *ngIf="!selection.hasValue()">
|
||||
<span class="desc">{{'ORG_DETAIL.TABLE.TOTAL' | translate}}</span>
|
||||
<span class="count">{{dataSource?.membersSubject.value.length}}</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="selection.hasValue()">
|
||||
<span class="desc">{{'ORG_DETAIL.TABLE.SELECTION' | translate}}</span>
|
||||
<span class="count">{{selection?.selected?.length}}</span>
|
||||
</ng-container>
|
||||
</div>
|
||||
<span class="fill-space"></span>
|
||||
<ng-template appHasRole
|
||||
[appHasRole]="['project.member.delete:' + project.projectId, 'project.member.delete']">
|
||||
<button (click)="removeProjectMemberSelection()" color="warn"
|
||||
matTooltip="{{'ORG_DETAIL.TABLE.DELETE' | translate}}" class="icon-button" mat-icon-button
|
||||
*ngIf="selection.hasValue()">
|
||||
<i class="las la-trash"></i>
|
||||
</button>
|
||||
</ng-template>
|
||||
<ng-template appHasRole
|
||||
[appHasRole]="['project.member.write:'+project.projectId,'project.member.write']">
|
||||
<a color="primary" [disabled]="disabled" class="add-button" (click)="openAddMember()"
|
||||
color="primary" mat-raised-button>
|
||||
<mat-icon class="icon">add</mat-icon>{{ 'ACTIONS.NEW' | translate }}
|
||||
</a>
|
||||
</ng-template>
|
||||
</div>
|
||||
<app-refresh-table *ngIf="project" (refreshed)="changePage()" [dataSize]="dataSource.totalResult"
|
||||
[selection]="selection">
|
||||
<ng-template appHasRole actions
|
||||
[appHasRole]="['project.member.delete:' + project.projectId, 'project.member.delete']">
|
||||
<button (click)="removeProjectMemberSelection()" color="warn"
|
||||
matTooltip="{{'ORG_DETAIL.TABLE.DELETE' | translate}}" class="icon-button" mat-icon-button
|
||||
*ngIf="selection.hasValue()">
|
||||
<i class="las la-trash"></i>
|
||||
</button>
|
||||
</ng-template>
|
||||
<ng-template appHasRole actions
|
||||
[appHasRole]="['project.member.write:'+project.projectId,'project.member.write']">
|
||||
<a color="primary" [disabled]="disabled" class="add-button" (click)="openAddMember()" color="primary"
|
||||
mat-raised-button>
|
||||
<mat-icon class="icon">add</mat-icon>{{ 'ACTIONS.NEW' | translate }}
|
||||
</a>
|
||||
</ng-template>
|
||||
|
||||
<div class="table-wrapper">
|
||||
<div class="spinner-container" *ngIf="dataSource?.loading$ | async">
|
||||
@@ -118,6 +106,6 @@
|
||||
(page)="changePage($event)">
|
||||
</mat-paginator>
|
||||
</div>
|
||||
</div>
|
||||
</app-refresh-table>
|
||||
</div>
|
||||
</div>
|
||||
@@ -16,6 +16,7 @@
|
||||
.right {
|
||||
flex: 1;
|
||||
padding-top: 1rem;
|
||||
padding-right: 1rem;
|
||||
|
||||
.head {
|
||||
display: flex;
|
||||
@@ -42,33 +43,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
.table-header-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.icon-button {
|
||||
margin-right: .5rem;
|
||||
}
|
||||
|
||||
.col {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.desc {
|
||||
font-size: .8rem;
|
||||
color: #8795a1;
|
||||
}
|
||||
.count {
|
||||
font-size: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
.fill-space {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.icon-button {
|
||||
margin-right: .5rem;
|
||||
}
|
||||
|
||||
.add-button {
|
||||
border-radius: .5rem;
|
||||
}
|
||||
.add-button {
|
||||
border-radius: .5rem;
|
||||
}
|
||||
|
||||
.table-wrapper {
|
||||
|
||||
@@ -170,7 +170,13 @@ export class ProjectMembersComponent {
|
||||
}
|
||||
}
|
||||
|
||||
public changePage(event: PageEvent): void {
|
||||
this.dataSource.loadMembers(this.project.projectId, this.projectType, event.pageIndex, event.pageSize, this.grantId);
|
||||
public changePage(event?: PageEvent): void {
|
||||
this.dataSource.loadMembers(
|
||||
this.project.projectId,
|
||||
this.projectType,
|
||||
event?.pageIndex ?? this.paginator.pageIndex,
|
||||
event?.pageSize ?? this.paginator.pageSize,
|
||||
this.grantId,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import { MatAutocompleteModule } from '@angular/material/autocomplete';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||
import { MatChipsModule } from '@angular/material/chips';
|
||||
import { MatDialogModule } from '@angular/material/dialog';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatPaginatorModule } from '@angular/material/paginator';
|
||||
@@ -17,6 +18,7 @@ import { TranslateModule } from '@ngx-translate/core';
|
||||
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
|
||||
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe.module';
|
||||
|
||||
import { RefreshTableModule } from '../refresh-table/refresh-table.module';
|
||||
import { ProjectMembersRoutingModule } from './project-members-routing.module';
|
||||
import { ProjectMembersComponent } from './project-members.component';
|
||||
|
||||
@@ -43,6 +45,8 @@ import { ProjectMembersComponent } from './project-members.component';
|
||||
FormsModule,
|
||||
TranslateModule,
|
||||
HasRolePipeModule,
|
||||
RefreshTableModule,
|
||||
MatDialogModule,
|
||||
],
|
||||
})
|
||||
export class ProjectMembersModule { }
|
||||
|
||||
@@ -1,79 +1,70 @@
|
||||
<div class="table-header-row" *ngIf="projectId">
|
||||
<div class="col">
|
||||
<ng-container *ngIf="!selection.hasValue()">
|
||||
<span class="desc">{{'ORG_DETAIL.TABLE.TOTAL' | translate}}</span>
|
||||
<span class="count">{{dataSource?.rolesSubject.value.length}}</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="selection.hasValue()">
|
||||
<span class="desc">{{'ORG_DETAIL.TABLE.SELECTION' | translate}}</span>
|
||||
<span class="count">{{selection?.selected?.length}}</span>
|
||||
</ng-container>
|
||||
</div>
|
||||
<span class="fill-space"></span>
|
||||
<ng-template appHasRole [appHasRole]="['project.role.delete']">
|
||||
<button color="warn" [disabled]="disabled" matTooltip="{{'PROJECT.ROLE.DELETE' | translate}}"
|
||||
class="icon-button" (click)="deleteSelectedRoles()" mat-icon-button
|
||||
<app-refresh-table *ngIf="projectId" (refreshed)="refreshPage()" [dataSize]="dataSource.totalResult"
|
||||
[selection]="selection">
|
||||
<ng-template appHasRole [appHasRole]="['project.role.delete']" actions>
|
||||
<button color="warn" class="icon-button" [disabled]="disabled"
|
||||
matTooltip="{{'PROJECT.ROLE.DELETE' | translate}}" (click)="deleteSelectedRoles()" mat-icon-button
|
||||
*ngIf="selection.hasValue() && actionsVisible">
|
||||
<i class="las la-trash"></i>
|
||||
</button>
|
||||
</ng-template>
|
||||
<ng-template appHasRole [appHasRole]="['project.role.write:' + projectId, 'project.role.write']">
|
||||
<a *ngIf="actionsVisible" [disabled]="disabled" class="add-button"
|
||||
<ng-template appHasRole [appHasRole]="['project.role.write:' + projectId, 'project.role.write']" actions>
|
||||
<a *ngIf="actionsVisible" [disabled]="disabled" class="rounded-button"
|
||||
[routerLink]="[ '/projects', projectId, 'roles', 'create']" color="primary" mat-raised-button>
|
||||
<mat-icon class="icon">add</mat-icon>{{ 'ACTIONS.NEW' | translate }}
|
||||
</a>
|
||||
</ng-template>
|
||||
</div>
|
||||
|
||||
<div class="table-wrapper">
|
||||
<div class="spinner-container" *ngIf="dataSource.loading$ | async">
|
||||
<mat-spinner diameter="50"></mat-spinner>
|
||||
<div class="table-wrapper">
|
||||
<div class="spinner-container" *ngIf="dataSource.loading$ | async">
|
||||
<mat-spinner diameter="50"></mat-spinner>
|
||||
</div>
|
||||
|
||||
<table [dataSource]="dataSource" mat-table class="full-width-table" matSort aria-label="Elements">
|
||||
<ng-container matColumnDef="select">
|
||||
<th class="selection" mat-header-cell *matHeaderCellDef>
|
||||
<mat-checkbox color="primary" (change)="$event ? masterToggle() : null"
|
||||
[checked]="selection.hasValue() && isAllSelected()"
|
||||
[indeterminate]="selection.hasValue() && !isAllSelected()">
|
||||
</mat-checkbox>
|
||||
</th>
|
||||
<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)">
|
||||
</mat-checkbox>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="key">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.ROLE.KEY' | translate }} </th>
|
||||
<td (click)="openDetailDialog(role)" mat-cell *matCellDef="let role"> {{role.key}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="displayname">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.ROLE.DISPLAY_NAME' | translate }} </th>
|
||||
<td (click)="openDetailDialog(role)" mat-cell *matCellDef="let role"> {{role.displayName}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="group">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.ROLE.GROUP' | translate }} </th>
|
||||
<td mat-cell *matCellDef="let role">
|
||||
<span class="role app-label" *ngIf="role.group" (click)="selectAllOfGroup(role.group)"
|
||||
[matTooltip]="'PROJECT.ROLE.SELECTGROUPTOOLTIP' | translate: role">{{role.group}}</span>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="creationDate">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.ROLE.CREATIONDATE' | translate }} </th>
|
||||
<td (click)="openDetailDialog(role)" mat-cell *matCellDef="let role">
|
||||
<span>{{role.creationDate | timestampToDate | date: 'dd. MMM, HH:mm' }}</span>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
</table>
|
||||
|
||||
<mat-paginator #paginator [length]="dataSource.totalResult" [pageSize]="50"
|
||||
[pageSizeOptions]="[25, 50, 100, 250]">
|
||||
</mat-paginator>
|
||||
</div>
|
||||
|
||||
<table [dataSource]="dataSource" mat-table class="full-width-table" matSort aria-label="Elements">
|
||||
<ng-container matColumnDef="select">
|
||||
<th class="selection" mat-header-cell *matHeaderCellDef>
|
||||
<mat-checkbox color="primary" (change)="$event ? masterToggle() : null"
|
||||
[checked]="selection.hasValue() && isAllSelected()"
|
||||
[indeterminate]="selection.hasValue() && !isAllSelected()">
|
||||
</mat-checkbox>
|
||||
</th>
|
||||
<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)">
|
||||
</mat-checkbox>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="key">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.ROLE.KEY' | translate }} </th>
|
||||
<td (click)="openDetailDialog(role)" mat-cell *matCellDef="let role"> {{role.key}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="displayname">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.ROLE.DISPLAY_NAME' | translate }} </th>
|
||||
<td (click)="openDetailDialog(role)" mat-cell *matCellDef="let role"> {{role.displayName}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="group">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.ROLE.GROUP' | translate }} </th>
|
||||
<td mat-cell *matCellDef="let role">
|
||||
<span class="role app-label" *ngIf="role.group" (click)="selectAllOfGroup(role.group)"
|
||||
[matTooltip]="'PROJECT.ROLE.SELECTGROUPTOOLTIP' | translate: role">{{role.group}}</span>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="creationDate">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.ROLE.CREATIONDATE' | translate }} </th>
|
||||
<td (click)="openDetailDialog(role)" mat-cell *matCellDef="let role">
|
||||
<span>{{role.creationDate | timestampToDate | date: 'dd. MMM, HH:mm' }}</span>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
</table>
|
||||
|
||||
<mat-paginator #paginator [length]="dataSource.totalResult" [pageSize]="50" [pageSizeOptions]="[25, 50, 100, 250]">
|
||||
</mat-paginator>
|
||||
</div>
|
||||
</app-refresh-table>
|
||||
@@ -1,31 +1,10 @@
|
||||
|
||||
.table-header-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.col {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.desc {
|
||||
font-size: .8rem;
|
||||
color: #8795a1;
|
||||
}
|
||||
.count {
|
||||
font-size: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
.fill-space {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.icon-button {
|
||||
margin-right: .5rem;
|
||||
}
|
||||
|
||||
.add-button {
|
||||
.rounded-button {
|
||||
border-radius: .5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.icon-button {
|
||||
margin-right: .5rem;
|
||||
}
|
||||
|
||||
.table-wrapper {
|
||||
|
||||
@@ -30,10 +30,11 @@ export class ProjectRolesComponent implements AfterViewInit, OnInit {
|
||||
/** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */
|
||||
public displayedColumns: string[] = ['select', 'key', 'displayname', 'group', 'creationDate'];
|
||||
|
||||
constructor(private projectService: ProjectService, private toast: ToastService, private dialog: MatDialog) { }
|
||||
constructor(private projectService: ProjectService, private toast: ToastService, private dialog: MatDialog) {
|
||||
this.dataSource = new ProjectRolesDataSource(this.projectService);
|
||||
}
|
||||
|
||||
public ngOnInit(): void {
|
||||
this.dataSource = new ProjectRolesDataSource(this.projectService);
|
||||
this.dataSource.loadRoles(this.projectId, 0, 25, 'asc');
|
||||
|
||||
this.selection.changed.subscribe(() => {
|
||||
@@ -119,4 +120,8 @@ export class ProjectRolesComponent implements AfterViewInit, OnInit {
|
||||
width: '400px',
|
||||
});
|
||||
}
|
||||
|
||||
public refreshPage(): void {
|
||||
this.dataSource.loadRoles(this.projectId, this.paginator.pageIndex, this.paginator.pageSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
|
||||
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe.module';
|
||||
import { TimestampToDatePipeModule } from 'src/app/pipes/timestamp-to-date-pipe.module';
|
||||
|
||||
import { RefreshTableModule } from '../refresh-table/refresh-table.module';
|
||||
import { ProjectRoleDetailComponent } from './project-role-detail/project-role-detail.component';
|
||||
import { ProjectRolesComponent } from './project-roles.component';
|
||||
|
||||
@@ -44,6 +45,7 @@ import { ProjectRolesComponent } from './project-roles.component';
|
||||
TranslateModule,
|
||||
MatMenuModule,
|
||||
TimestampToDatePipeModule,
|
||||
RefreshTableModule,
|
||||
],
|
||||
exports: [
|
||||
ProjectRolesComponent,
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
<div class="table-header-row">
|
||||
<div class="col">
|
||||
<ng-container *ngIf="!selection.hasValue()">
|
||||
<span class="desc">{{'ORG_DETAIL.TABLE.TOTAL' | translate}}</span>
|
||||
<span class="count">{{dataSize}}</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="selection.hasValue()">
|
||||
<span class="desc">{{'ORG_DETAIL.TABLE.SELECTION' | translate}}</span>
|
||||
<span class="count">{{selection?.selected?.length}}</span>
|
||||
</ng-container>
|
||||
</div>
|
||||
<span class="fill-space"></span>
|
||||
<button mat-icon-button (click)="emitRefresh()" class="icon-button" matTooltip="{{'ACTIONS.REFRESH' | translate}}">
|
||||
<mat-icon>refresh</mat-icon>
|
||||
</button>
|
||||
<ng-content select="[actions]"></ng-content>
|
||||
</div>
|
||||
<ng-content></ng-content>
|
||||
@@ -0,0 +1,26 @@
|
||||
|
||||
.table-header-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.col {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.desc {
|
||||
font-size: .8rem;
|
||||
color: #8795a1;
|
||||
}
|
||||
.count {
|
||||
font-size: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
.fill-space {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.icon-button {
|
||||
margin-right: .5rem;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { RefreshTableComponent } from './refresh-table.component';
|
||||
|
||||
describe('RefreshTableComponent', () => {
|
||||
let component: RefreshTableComponent;
|
||||
let fixture: ComponentFixture<RefreshTableComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [RefreshTableComponent],
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(RefreshTableComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,46 @@
|
||||
import { animate, animation, keyframes, style, transition, trigger, useAnimation } from '@angular/animations';
|
||||
import { SelectionModel } from '@angular/cdk/collections';
|
||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
|
||||
const rotate = animation([
|
||||
animate(
|
||||
'{{time}} cubic-bezier(0.785, 0.135, 0.15, 0.86)',
|
||||
keyframes([
|
||||
style({
|
||||
transform: 'rotate(0deg)',
|
||||
}),
|
||||
style({
|
||||
transform: 'rotate(360deg)',
|
||||
}),
|
||||
]),
|
||||
),
|
||||
]);
|
||||
@Component({
|
||||
selector: 'app-refresh-table',
|
||||
templateUrl: './refresh-table.component.html',
|
||||
styleUrls: ['./refresh-table.component.scss'],
|
||||
animations: [
|
||||
trigger('rotate', [
|
||||
transition('* => *', [useAnimation(rotate, { params: { time: '1s' } })]),
|
||||
]),
|
||||
],
|
||||
})
|
||||
export class RefreshTableComponent implements OnInit {
|
||||
@Input() public selection: SelectionModel<any> = new SelectionModel<any>(true, []);
|
||||
@Input() public dataSize: number = 0;
|
||||
@Input() public emitRefreshAfterTimeoutInMs: number = 0;
|
||||
@Output() public refreshed: EventEmitter<void> = new EventEmitter();
|
||||
|
||||
ngOnInit(): void {
|
||||
if (this.emitRefreshAfterTimeoutInMs) {
|
||||
setTimeout(() => {
|
||||
this.emitRefresh();
|
||||
}, this.emitRefreshAfterTimeoutInMs);
|
||||
}
|
||||
}
|
||||
|
||||
emitRefresh(): void {
|
||||
this.selection.clear();
|
||||
return this.refreshed.emit();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
|
||||
import { RefreshTableComponent } from './refresh-table.component';
|
||||
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [RefreshTableComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
MatButtonModule,
|
||||
MatIconModule,
|
||||
TranslateModule,
|
||||
FormsModule,
|
||||
MatTooltipModule,
|
||||
],
|
||||
exports: [
|
||||
RefreshTableComponent,
|
||||
],
|
||||
})
|
||||
export class RefreshTableModule { }
|
||||
@@ -1,148 +1,119 @@
|
||||
<div class="table-header-row">
|
||||
<div class="col">
|
||||
<ng-container *ngIf="!selection.hasValue()">
|
||||
<span class="desc">{{'ORG_DETAIL.TABLE.TOTAL' | translate}}</span>
|
||||
<span class="count">{{dataSource.grantsSubject.value.length}}</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="selection.hasValue()">
|
||||
<span class="desc">{{'ORG_DETAIL.TABLE.SELECTION' | translate}}</span>
|
||||
<span class="count">{{selection?.selected?.length}}</span>
|
||||
</ng-container>
|
||||
</div>
|
||||
<span class="fill-space"></span>
|
||||
<button color="warn" matTooltip="{{'GRANTS.DELETE' | translate}}" class="icon-button" mat-icon-button
|
||||
<app-refresh-table (refreshed)="changePage()" [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>
|
||||
</button>
|
||||
<a *ngIf="allowCreate && context !== UserGrantContext.USER" matTooltip="{{'GRANTS.ADD' | translate}}"
|
||||
<a *ngIf="allowCreate && context !== UserGrantContext.USER" matTooltip="{{'GRANTS.ADD' | translate}}" actions
|
||||
color="primary" class="add-button" color="primary" mat-raised-button [routerLink]="routerLink">
|
||||
<mat-icon class="icon">add</mat-icon>{{ 'GRANTS.ADD_BTN' | translate }}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="table-wrapper">
|
||||
<div class="spinner-container" *ngIf="dataSource.loading$ | async">
|
||||
<mat-spinner diameter="50"></mat-spinner>
|
||||
<div class="table-wrapper">
|
||||
<div class="spinner-container" *ngIf="dataSource.loading$ | async">
|
||||
<mat-spinner diameter="50"></mat-spinner>
|
||||
</div>
|
||||
<table mat-table multiTemplateDataRows class="full-width-table" aria-label="Elements" [dataSource]="dataSource">
|
||||
<ng-container matColumnDef="select">
|
||||
<th class="selection" mat-header-cell *matHeaderCellDef>
|
||||
<mat-checkbox color="primary" (change)="$event ? masterToggle() : null"
|
||||
[checked]="selection.hasValue() && isAllSelected()"
|
||||
[indeterminate]="selection.hasValue() && !isAllSelected()">
|
||||
</mat-checkbox>
|
||||
</th>
|
||||
<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>
|
||||
</mat-checkbox>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
|
||||
<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>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="org">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.GRANT.GRANTEDORGDOMAIN' | translate }} </th>
|
||||
<td class="pointer" mat-cell *matCellDef="let grant">
|
||||
{{grant.orgName}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="projectId">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.GRANT.PROJECTNAME' | translate }} </th>
|
||||
<td class="pointer" mat-cell *matCellDef="let grant">
|
||||
{{grant.projectName}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="creationDate">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.GRANT.CREATIONDATE' | translate }} </th>
|
||||
<td class="pointer" mat-cell *matCellDef="let grant">
|
||||
{{grant.creationDate | timestampToDate | date: 'dd. MMM, HH:mm' }} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="changeDate">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.GRANT.CHANGEDATE' | translate }} </th>
|
||||
<td class="pointer" mat-cell *matCellDef="let grant">
|
||||
{{grant.changeDate | timestampToDate | date: 'dd. MMM, HH:mm' }} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="roleNamesList">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.GRANT.ROLENAMESLIST' | translate }} </th>
|
||||
<td mat-cell *matCellDef="let grant; let i = index">
|
||||
<ng-container *ngIf="context === UserGrantContext.USER">
|
||||
<span class="no-roles"
|
||||
*ngIf="grant.roleKeysList?.length === 0">{{'PROJECT.GRANT.NOROLES' | translate}}</span>
|
||||
<span
|
||||
*ngFor="let role of grant.roleKeysList">{{ (role.length>8)? (role | slice:0:8)+'..':(role) }}</span>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="context === UserGrantContext.OWNED_PROJECT">
|
||||
<ng-container *ngIf="loadedProjectId !== grant.projectId">
|
||||
<span class="role app-label"
|
||||
*ngFor="let role of grant.roleKeysList">{{ (role.length>6)? (role | slice:0:6)+'..':(role) }}</span>
|
||||
<button mat-icon-button (click)="getProjectRoleOptions(grant.projectId)"
|
||||
matTooltip="{{'ACTIONS.CHANGE' | translate}}">
|
||||
<i class="las la-edit"></i>
|
||||
</button>
|
||||
</ng-container>
|
||||
<mat-form-field class="form-field" appearance="outline"
|
||||
*ngIf="loadedProjectId === grant.projectId">
|
||||
<mat-label>{{ 'PROJECT.GRANT.ROLENAMESLIST' | translate }}</mat-label>
|
||||
<mat-select [(ngModel)]="grant.roleKeysList" multiple [disabled]="allowCreate == false"
|
||||
(selectionChange)="updateRoles(grant, $event)">
|
||||
<mat-option *ngFor="let role of projectRoleOptions" [value]="role.key">
|
||||
{{role.key}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="context === UserGrantContext.GRANTED_PROJECT">
|
||||
<mat-form-field class="form-field" appearance="outline">
|
||||
<mat-label>{{ 'PROJECT.GRANT.ROLENAMESLIST' | translate }}</mat-label>
|
||||
<mat-select [(ngModel)]="grant.roleKeysList" multiple [disabled]="allowCreate == false"
|
||||
(selectionChange)="updateRoles(grant, $event)">
|
||||
<mat-option *ngFor="let role of grantRoleOptions" [value]="role">
|
||||
{{role}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</ng-container>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;" class="element-row">
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<mat-paginator #paginator [length]="dataSource.totalResult" [pageSize]="50" [length]="dataSource.totalResult"
|
||||
[pageSizeOptions]="[2, 3, 25, 50, 100, 250]" (page)="changePage($event)">
|
||||
</mat-paginator>
|
||||
</div>
|
||||
<table mat-table multiTemplateDataRows class="full-width-table" aria-label="Elements" [dataSource]="dataSource">
|
||||
<ng-container matColumnDef="select">
|
||||
<th class="selection" mat-header-cell *matHeaderCellDef>
|
||||
<mat-checkbox color="primary" (change)="$event ? masterToggle() : null"
|
||||
[checked]="selection.hasValue() && isAllSelected()"
|
||||
[indeterminate]="selection.hasValue() && !isAllSelected()">
|
||||
</mat-checkbox>
|
||||
</th>
|
||||
<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>
|
||||
</mat-checkbox>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
|
||||
<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>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="org">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.GRANT.GRANTEDORGDOMAIN' | translate }} </th>
|
||||
<td class="pointer" mat-cell *matCellDef="let grant">
|
||||
{{grant.orgName}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="projectId">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.GRANT.PROJECTNAME' | translate }} </th>
|
||||
<td class="pointer" mat-cell *matCellDef="let grant">
|
||||
{{grant.projectName}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="creationDate">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.GRANT.CREATIONDATE' | translate }} </th>
|
||||
<td class="pointer" mat-cell *matCellDef="let grant">
|
||||
{{grant.creationDate | timestampToDate | date: 'dd. MMM, HH:mm' }} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="changeDate">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.GRANT.CHANGEDATE' | translate }} </th>
|
||||
<td class="pointer" mat-cell *matCellDef="let grant">
|
||||
{{grant.changeDate | timestampToDate | date: 'dd. MMM, HH:mm' }} </td>
|
||||
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="roleNamesList">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.GRANT.ROLENAMESLIST' | translate }} </th>
|
||||
<td mat-cell *matCellDef="let grant; let i = index">
|
||||
<ng-container *ngIf="context === UserGrantContext.USER">
|
||||
<span class="no-roles"
|
||||
*ngIf="grant.roleKeysList?.length === 0">{{'PROJECT.GRANT.NOROLES' | translate}}</span>
|
||||
<span
|
||||
*ngFor="let role of grant.roleKeysList">{{ (role.length>8)? (role | slice:0:8)+'..':(role) }}</span>
|
||||
</ng-container>
|
||||
|
||||
<!-- <ng-container *ngIf="context === UserGrantContext.USER">
|
||||
<ng-container *ngIf="loadedGrantId !== grant.grantId">
|
||||
<span class="role app-label" *ngFor="let role of grant.roleKeysList">{{role}}</span>
|
||||
<button mat-icon-button (click)="getGrantRoleOptions(grant.grantId, grant.projectId)"
|
||||
matTooltip="{{'ACTIONS.CHANGE' | translate}}">
|
||||
<i class="las la-edit"></i>
|
||||
</button>
|
||||
</ng-container>
|
||||
<mat-form-field class="form-field" appearance="outline" *ngIf="loadedGrantId === grant.grantId">
|
||||
<mat-label>{{ 'PROJECT.GRANT.ROLENAMESLIST' | translate }}</mat-label>
|
||||
<mat-select [(ngModel)]="grant.roleKeysList" multiple [disabled]="allowCreate == false"
|
||||
(selectionChange)="updateRoles(grant, $event)">
|
||||
<mat-option *ngFor="let role of grantRoleOptions" [value]="role">
|
||||
{{role}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</ng-container> -->
|
||||
|
||||
<ng-container *ngIf="context === UserGrantContext.OWNED_PROJECT">
|
||||
<ng-container *ngIf="loadedProjectId !== grant.projectId">
|
||||
<span class="role app-label"
|
||||
*ngFor="let role of grant.roleKeysList">{{ (role.length>6)? (role | slice:0:6)+'..':(role) }}</span>
|
||||
<button mat-icon-button (click)="getProjectRoleOptions(grant.projectId)"
|
||||
matTooltip="{{'ACTIONS.CHANGE' | translate}}">
|
||||
<i class="las la-edit"></i>
|
||||
</button>
|
||||
</ng-container>
|
||||
<mat-form-field class="form-field" appearance="outline" *ngIf="loadedProjectId === grant.projectId">
|
||||
<mat-label>{{ 'PROJECT.GRANT.ROLENAMESLIST' | translate }}</mat-label>
|
||||
<mat-select [(ngModel)]="grant.roleKeysList" multiple [disabled]="allowCreate == false"
|
||||
(selectionChange)="updateRoles(grant, $event)">
|
||||
<mat-option *ngFor="let role of projectRoleOptions" [value]="role.key">
|
||||
{{role.key}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="context === UserGrantContext.GRANTED_PROJECT">
|
||||
<mat-form-field class="form-field" appearance="outline">
|
||||
<mat-label>{{ 'PROJECT.GRANT.ROLENAMESLIST' | translate }}</mat-label>
|
||||
<mat-select [(ngModel)]="grant.roleKeysList" multiple [disabled]="allowCreate == false"
|
||||
(selectionChange)="updateRoles(grant, $event)">
|
||||
<mat-option *ngFor="let role of grantRoleOptions" [value]="role">
|
||||
{{role}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</ng-container>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;" class="element-row">
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<mat-paginator #paginator [length]="dataSource.totalResult" [pageSize]="50" [length]="dataSource.totalResult"
|
||||
[pageSizeOptions]="[2, 3, 25, 50, 100, 250]" (page)="changePage($event)">
|
||||
</mat-paginator>
|
||||
</div>
|
||||
</app-refresh-table>
|
||||
@@ -1,31 +1,6 @@
|
||||
|
||||
.table-header-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.col {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.desc {
|
||||
font-size: .8rem;
|
||||
color: #8795a1;
|
||||
}
|
||||
.count {
|
||||
font-size: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
.fill-space {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.icon-button {
|
||||
margin-right: .5rem;
|
||||
}
|
||||
|
||||
.add-button {
|
||||
border-radius: .5rem;
|
||||
}
|
||||
.add-button {
|
||||
border-radius: .5rem;
|
||||
}
|
||||
|
||||
.table-wrapper {
|
||||
|
||||
@@ -176,11 +176,16 @@ export class UserGrantsComponent implements OnInit, AfterViewInit {
|
||||
});
|
||||
}
|
||||
|
||||
public changePage(event: PageEvent): void {
|
||||
this.dataSource.loadGrants(this.context, event.pageIndex, event.pageSize, {
|
||||
projectId: this.projectId,
|
||||
grantId: this.grantId,
|
||||
userId: this.userId,
|
||||
});
|
||||
public changePage(event?: PageEvent): void {
|
||||
this.dataSource.loadGrants(
|
||||
this.context,
|
||||
event?.pageIndex ?? this.paginator.pageIndex,
|
||||
event?.pageSize ?? this.paginator.pageSize,
|
||||
{
|
||||
projectId: this.projectId,
|
||||
grantId: this.grantId,
|
||||
userId: this.userId,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe.module';
|
||||
import { TimestampToDatePipeModule } from 'src/app/pipes/timestamp-to-date-pipe.module';
|
||||
|
||||
import { AvatarModule } from '../avatar/avatar.module';
|
||||
import { RefreshTableModule } from '../refresh-table/refresh-table.module';
|
||||
import { UserGrantsComponent } from './user-grants.component';
|
||||
|
||||
|
||||
@@ -41,6 +42,7 @@ import { UserGrantsComponent } from './user-grants.component';
|
||||
TranslateModule,
|
||||
HasRolePipeModule,
|
||||
TimestampToDatePipeModule,
|
||||
RefreshTableModule,
|
||||
],
|
||||
exports: [
|
||||
UserGrantsComponent,
|
||||
|
||||
Reference in New Issue
Block a user