mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-12 08:37:32 +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 <florian@caos.ch> * Update console/src/assets/i18n/en.json Co-authored-by: Florian Forster <florian@caos.ch> * z-index, new colors * new senf color Co-authored-by: Florian Forster <florian@caos.ch>
This commit is contained in:
@@ -27,7 +27,6 @@
|
|||||||
"src/favicon.ico",
|
"src/favicon.ico",
|
||||||
"src/assets",
|
"src/assets",
|
||||||
"src/manifest.webmanifest",
|
"src/manifest.webmanifest",
|
||||||
"src/404.html"
|
|
||||||
],
|
],
|
||||||
"styles": [
|
"styles": [
|
||||||
"src/styles.scss"
|
"src/styles.scss"
|
||||||
|
@@ -1,23 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
|
|
||||||
<title>caos console</title>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
sessionStorage.redirect = location.href;
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<meta http-equiv="refresh" content="0;URL='/'"></meta>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</body>
|
|
||||||
|
|
||||||
|
|
||||||
</html>
|
|
@@ -47,7 +47,6 @@
|
|||||||
</app-accounts-card>
|
</app-accounts-card>
|
||||||
</div>
|
</div>
|
||||||
</mat-toolbar>
|
</mat-toolbar>
|
||||||
|
|
||||||
<mat-drawer-container class="main-container">
|
<mat-drawer-container class="main-container">
|
||||||
<mat-drawer #drawer class="sidenav" [mode]="(isHandset$ | async) ? 'over' : 'side'"
|
<mat-drawer #drawer class="sidenav" [mode]="(isHandset$ | async) ? 'over' : 'side'"
|
||||||
[opened]="!(isHandset$ | async)">
|
[opened]="!(isHandset$ | async)">
|
||||||
|
@@ -1,7 +1,8 @@
|
|||||||
import { animate, group, query, style, transition, trigger } from '@angular/animations';
|
import { animate, group, query, style, transition, trigger } from '@angular/animations';
|
||||||
import { BreakpointObserver } from '@angular/cdk/layout';
|
import { BreakpointObserver } from '@angular/cdk/layout';
|
||||||
import { OverlayContainer } from '@angular/cdk/overlay';
|
import { OverlayContainer } from '@angular/cdk/overlay';
|
||||||
import { Component, HostBinding, OnDestroy, ViewChild } from '@angular/core';
|
import { ViewportScroller } from '@angular/common';
|
||||||
|
import { Component, HostBinding, Inject, OnDestroy, ViewChild } from '@angular/core';
|
||||||
import { MatIconRegistry } from '@angular/material/icon';
|
import { MatIconRegistry } from '@angular/material/icon';
|
||||||
import { MatDrawer } from '@angular/material/sidenav';
|
import { MatDrawer } from '@angular/material/sidenav';
|
||||||
import { DomSanitizer } from '@angular/platform-browser';
|
import { DomSanitizer } from '@angular/platform-browser';
|
||||||
@@ -138,6 +139,8 @@ export class AppComponent implements OnDestroy {
|
|||||||
private orgSub: Subscription = new Subscription();
|
private orgSub: Subscription = new Subscription();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
public viewPortScroller: ViewportScroller,
|
||||||
|
@Inject('windowObject') public window: Window,
|
||||||
public translate: TranslateService,
|
public translate: TranslateService,
|
||||||
public authService: AuthService,
|
public authService: AuthService,
|
||||||
private breakpointObserver: BreakpointObserver,
|
private breakpointObserver: BreakpointObserver,
|
||||||
|
@@ -150,6 +150,7 @@ const authConfig: AuthConfig = {
|
|||||||
GrpcService,
|
GrpcService,
|
||||||
AuthService,
|
AuthService,
|
||||||
AuthUserService,
|
AuthUserService,
|
||||||
|
{ provide: 'windowObject', useValue: window },
|
||||||
],
|
],
|
||||||
bootstrap: [AppComponent],
|
bootstrap: [AppComponent],
|
||||||
})
|
})
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
<div class="avatar-circle dontcloseonclick"
|
<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}">
|
[ngClass]="{'active': active}">
|
||||||
{{credentials}}
|
{{credentials}}
|
||||||
</div>
|
</div>
|
@@ -30,22 +30,22 @@ export class AvatarComponent implements OnInit {
|
|||||||
|
|
||||||
getColor(userName: string): string {
|
getColor(userName: string): string {
|
||||||
const colors = [
|
const colors = [
|
||||||
'#e51c23',
|
'#B44D51',
|
||||||
'#e91e63',
|
'#B75073',
|
||||||
'#9c27b0',
|
'#84498E',
|
||||||
'#673ab7',
|
'#705998',
|
||||||
'#3f51b5',
|
'#5C6598',
|
||||||
'#5677fc',
|
'#7F90D3',
|
||||||
'#03a9f4',
|
'#3E93B9',
|
||||||
'#00bcd4',
|
'#3494A0',
|
||||||
'#009688',
|
'#25716A',
|
||||||
'#259b24',
|
'#427E41',
|
||||||
'#8bc34a',
|
'#89A568',
|
||||||
'#afb42b',
|
'#90924D',
|
||||||
'#ff9800',
|
'#E2B032',
|
||||||
'#ff5722',
|
'#C97358',
|
||||||
'#795548',
|
'#6D5B54',
|
||||||
'#607d8b',
|
'#6B7980',
|
||||||
];
|
];
|
||||||
|
|
||||||
let hash = 0;
|
let hash = 0;
|
||||||
|
@@ -4,8 +4,8 @@
|
|||||||
<div class="people">
|
<div class="people">
|
||||||
<div class="img-list">
|
<div class="img-list">
|
||||||
<ng-container *ngIf="totalResult < 10; else compact">
|
<ng-container *ngIf="totalResult < 10; else compact">
|
||||||
<ng-container *ngFor="let member of membersSubject | async">
|
<ng-container *ngFor="let member of membersSubject | async; index as i">
|
||||||
<div (click)="showDetail()" class="avatar-circle"
|
<div (click)="showDetail()" class="avatar-circle" [ngStyle]="{'z-index': 100 - i}"
|
||||||
matTooltip="{{ member.email }} | {{member.rolesList?.join(' ')}}">
|
matTooltip="{{ member.email }} | {{member.rolesList?.join(' ')}}">
|
||||||
<app-avatar *ngIf="member && member.firstName && member.lastName; else thumbavatar"
|
<app-avatar *ngIf="member && member.firstName && member.lastName; else thumbavatar"
|
||||||
class="avatar dontcloseonclick" [name]="member.firstName + ' '+ member.lastName"
|
class="avatar dontcloseonclick" [name]="member.firstName + ' '+ member.lastName"
|
||||||
|
@@ -28,18 +28,18 @@
|
|||||||
margin-left: 1rem;
|
margin-left: 1rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
transition: all .15s ease-in-out;
|
||||||
|
|
||||||
.avatar-circle {
|
.avatar-circle {
|
||||||
|
transition: all .3s ease-in-out;
|
||||||
float: left;
|
float: left;
|
||||||
margin: 0 8px 0 -15px;
|
margin: 0 8px 0 -15px;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
width: 32px;
|
width: 32px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
&:not(:first-child) {
|
-webkit-box-shadow: 2px 0px 7px -1px rgba(33,34,36,.5);
|
||||||
-webkit-box-shadow: -9px 0px 20px -6px rgba(0,0,0,0.75);
|
-moz-box-shadow: 2px 0px 7px -1px rgba(33,34,36,.5);
|
||||||
-moz-box-shadow: -9px 0px 20px -6px rgba(0,0,0,0.75);
|
box-shadow: 2px 0px 7px -1px rgba(33,34,36,.5);
|
||||||
box-shadow: -9px 0px 20px -6px rgba(0,0,0,0.75);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.add-img {
|
.add-img {
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
<div class="max-width-container">
|
<div class="container">
|
||||||
<div class="container">
|
|
||||||
<div class="left">
|
<div class="left">
|
||||||
<a *ngIf="project" [routerLink]="[ '/projects', project.projectId]" mat-icon-button>
|
<a *ngIf="project" [routerLink]="[ '/projects', project.projectId]" mat-icon-button>
|
||||||
<mat-icon class="icon">arrow_back</mat-icon>
|
<mat-icon class="icon">arrow_back</mat-icon>
|
||||||
@@ -11,19 +10,9 @@
|
|||||||
<p class="desc">{{ 'PROJECT.MEMBER.DESCRIPTION' | translate }}</p>
|
<p class="desc">{{ 'PROJECT.MEMBER.DESCRIPTION' | translate }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="table-header-row" *ngIf="project">
|
<app-refresh-table *ngIf="project" (refreshed)="changePage()" [dataSize]="dataSource.totalResult"
|
||||||
<div class="col">
|
[selection]="selection">
|
||||||
<ng-container *ngIf="!selection.hasValue()">
|
<ng-template appHasRole actions
|
||||||
<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']">
|
[appHasRole]="['project.member.delete:' + project.projectId, 'project.member.delete']">
|
||||||
<button (click)="removeProjectMemberSelection()" color="warn"
|
<button (click)="removeProjectMemberSelection()" color="warn"
|
||||||
matTooltip="{{'ORG_DETAIL.TABLE.DELETE' | translate}}" class="icon-button" mat-icon-button
|
matTooltip="{{'ORG_DETAIL.TABLE.DELETE' | translate}}" class="icon-button" mat-icon-button
|
||||||
@@ -31,14 +20,13 @@
|
|||||||
<i class="las la-trash"></i>
|
<i class="las la-trash"></i>
|
||||||
</button>
|
</button>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
<ng-template appHasRole
|
<ng-template appHasRole actions
|
||||||
[appHasRole]="['project.member.write:'+project.projectId,'project.member.write']">
|
[appHasRole]="['project.member.write:'+project.projectId,'project.member.write']">
|
||||||
<a color="primary" [disabled]="disabled" class="add-button" (click)="openAddMember()"
|
<a color="primary" [disabled]="disabled" class="add-button" (click)="openAddMember()" color="primary"
|
||||||
color="primary" mat-raised-button>
|
mat-raised-button>
|
||||||
<mat-icon class="icon">add</mat-icon>{{ 'ACTIONS.NEW' | translate }}
|
<mat-icon class="icon">add</mat-icon>{{ 'ACTIONS.NEW' | translate }}
|
||||||
</a>
|
</a>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="table-wrapper">
|
<div class="table-wrapper">
|
||||||
<div class="spinner-container" *ngIf="dataSource?.loading$ | async">
|
<div class="spinner-container" *ngIf="dataSource?.loading$ | async">
|
||||||
@@ -118,6 +106,6 @@
|
|||||||
(page)="changePage($event)">
|
(page)="changePage($event)">
|
||||||
</mat-paginator>
|
</mat-paginator>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</app-refresh-table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
@@ -16,6 +16,7 @@
|
|||||||
.right {
|
.right {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding-top: 1rem;
|
padding-top: 1rem;
|
||||||
|
padding-right: 1rem;
|
||||||
|
|
||||||
.head {
|
.head {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -42,33 +43,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-header-row {
|
.icon-button {
|
||||||
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;
|
margin-right: .5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.add-button {
|
.add-button {
|
||||||
border-radius: .5rem;
|
border-radius: .5rem;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-wrapper {
|
.table-wrapper {
|
||||||
|
@@ -170,7 +170,13 @@ export class ProjectMembersComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public changePage(event: PageEvent): void {
|
public changePage(event?: PageEvent): void {
|
||||||
this.dataSource.loadMembers(this.project.projectId, this.projectType, event.pageIndex, event.pageSize, this.grantId);
|
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 { MatButtonModule } from '@angular/material/button';
|
||||||
import { MatCheckboxModule } from '@angular/material/checkbox';
|
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||||
import { MatChipsModule } from '@angular/material/chips';
|
import { MatChipsModule } from '@angular/material/chips';
|
||||||
|
import { MatDialogModule } from '@angular/material/dialog';
|
||||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||||
import { MatIconModule } from '@angular/material/icon';
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
import { MatPaginatorModule } from '@angular/material/paginator';
|
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 { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
|
||||||
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe.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 { ProjectMembersRoutingModule } from './project-members-routing.module';
|
||||||
import { ProjectMembersComponent } from './project-members.component';
|
import { ProjectMembersComponent } from './project-members.component';
|
||||||
|
|
||||||
@@ -43,6 +45,8 @@ import { ProjectMembersComponent } from './project-members.component';
|
|||||||
FormsModule,
|
FormsModule,
|
||||||
TranslateModule,
|
TranslateModule,
|
||||||
HasRolePipeModule,
|
HasRolePipeModule,
|
||||||
|
RefreshTableModule,
|
||||||
|
MatDialogModule,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class ProjectMembersModule { }
|
export class ProjectMembersModule { }
|
||||||
|
@@ -1,31 +1,20 @@
|
|||||||
<div class="table-header-row" *ngIf="projectId">
|
<app-refresh-table *ngIf="projectId" (refreshed)="refreshPage()" [dataSize]="dataSource.totalResult"
|
||||||
<div class="col">
|
[selection]="selection">
|
||||||
<ng-container *ngIf="!selection.hasValue()">
|
<ng-template appHasRole [appHasRole]="['project.role.delete']" actions>
|
||||||
<span class="desc">{{'ORG_DETAIL.TABLE.TOTAL' | translate}}</span>
|
<button color="warn" class="icon-button" [disabled]="disabled"
|
||||||
<span class="count">{{dataSource?.rolesSubject.value.length}}</span>
|
matTooltip="{{'PROJECT.ROLE.DELETE' | translate}}" (click)="deleteSelectedRoles()" mat-icon-button
|
||||||
</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
|
|
||||||
*ngIf="selection.hasValue() && actionsVisible">
|
*ngIf="selection.hasValue() && actionsVisible">
|
||||||
<i class="las la-trash"></i>
|
<i class="las la-trash"></i>
|
||||||
</button>
|
</button>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
<ng-template appHasRole [appHasRole]="['project.role.write:' + projectId, 'project.role.write']">
|
<ng-template appHasRole [appHasRole]="['project.role.write:' + projectId, 'project.role.write']" actions>
|
||||||
<a *ngIf="actionsVisible" [disabled]="disabled" class="add-button"
|
<a *ngIf="actionsVisible" [disabled]="disabled" class="rounded-button"
|
||||||
[routerLink]="[ '/projects', projectId, 'roles', 'create']" color="primary" mat-raised-button>
|
[routerLink]="[ '/projects', projectId, 'roles', 'create']" color="primary" mat-raised-button>
|
||||||
<mat-icon class="icon">add</mat-icon>{{ 'ACTIONS.NEW' | translate }}
|
<mat-icon class="icon">add</mat-icon>{{ 'ACTIONS.NEW' | translate }}
|
||||||
</a>
|
</a>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="table-wrapper">
|
<div class="table-wrapper">
|
||||||
<div class="spinner-container" *ngIf="dataSource.loading$ | async">
|
<div class="spinner-container" *ngIf="dataSource.loading$ | async">
|
||||||
<mat-spinner diameter="50"></mat-spinner>
|
<mat-spinner diameter="50"></mat-spinner>
|
||||||
</div>
|
</div>
|
||||||
@@ -74,6 +63,8 @@
|
|||||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<mat-paginator #paginator [length]="dataSource.totalResult" [pageSize]="50" [pageSizeOptions]="[25, 50, 100, 250]">
|
<mat-paginator #paginator [length]="dataSource.totalResult" [pageSize]="50"
|
||||||
|
[pageSizeOptions]="[25, 50, 100, 250]">
|
||||||
</mat-paginator>
|
</mat-paginator>
|
||||||
</div>
|
</div>
|
||||||
|
</app-refresh-table>
|
@@ -1,31 +1,10 @@
|
|||||||
|
|
||||||
.table-header-row {
|
.rounded-button {
|
||||||
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;
|
border-radius: .5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.icon-button {
|
||||||
|
margin-right: .5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-wrapper {
|
.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. */
|
/** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */
|
||||||
public displayedColumns: string[] = ['select', 'key', 'displayname', 'group', 'creationDate'];
|
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 {
|
public ngOnInit(): void {
|
||||||
this.dataSource = new ProjectRolesDataSource(this.projectService);
|
|
||||||
this.dataSource.loadRoles(this.projectId, 0, 25, 'asc');
|
this.dataSource.loadRoles(this.projectId, 0, 25, 'asc');
|
||||||
|
|
||||||
this.selection.changed.subscribe(() => {
|
this.selection.changed.subscribe(() => {
|
||||||
@@ -119,4 +120,8 @@ export class ProjectRolesComponent implements AfterViewInit, OnInit {
|
|||||||
width: '400px',
|
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 { HasRolePipeModule } from 'src/app/pipes/has-role-pipe.module';
|
||||||
import { TimestampToDatePipeModule } from 'src/app/pipes/timestamp-to-date-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 { ProjectRoleDetailComponent } from './project-role-detail/project-role-detail.component';
|
||||||
import { ProjectRolesComponent } from './project-roles.component';
|
import { ProjectRolesComponent } from './project-roles.component';
|
||||||
|
|
||||||
@@ -44,6 +45,7 @@ import { ProjectRolesComponent } from './project-roles.component';
|
|||||||
TranslateModule,
|
TranslateModule,
|
||||||
MatMenuModule,
|
MatMenuModule,
|
||||||
TimestampToDatePipeModule,
|
TimestampToDatePipeModule,
|
||||||
|
RefreshTableModule,
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
ProjectRolesComponent,
|
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,26 +1,14 @@
|
|||||||
<div class="table-header-row">
|
<app-refresh-table (refreshed)="changePage()" [dataSize]="dataSource.totalResult" [selection]="selection">
|
||||||
<div class="col">
|
<button color="warn" matTooltip="{{'GRANTS.DELETE' | translate}}" class="icon-button" mat-icon-button actions
|
||||||
<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
|
|
||||||
(click)="deleteGrantSelection()" *ngIf="selection.hasValue() && allowDelete">
|
(click)="deleteGrantSelection()" *ngIf="selection.hasValue() && allowDelete">
|
||||||
<i class="las la-trash"></i>
|
<i class="las la-trash"></i>
|
||||||
</button>
|
</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">
|
color="primary" class="add-button" color="primary" mat-raised-button [routerLink]="routerLink">
|
||||||
<mat-icon class="icon">add</mat-icon>{{ 'GRANTS.ADD_BTN' | translate }}
|
<mat-icon class="icon">add</mat-icon>{{ 'GRANTS.ADD_BTN' | translate }}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="table-wrapper">
|
<div class="table-wrapper">
|
||||||
<div class="spinner-container" *ngIf="dataSource.loading$ | async">
|
<div class="spinner-container" *ngIf="dataSource.loading$ | async">
|
||||||
<mat-spinner diameter="50"></mat-spinner>
|
<mat-spinner diameter="50"></mat-spinner>
|
||||||
</div>
|
</div>
|
||||||
@@ -36,7 +24,8 @@
|
|||||||
<mat-checkbox color="primary" (click)="$event.stopPropagation()"
|
<mat-checkbox color="primary" (click)="$event.stopPropagation()"
|
||||||
(change)="$event ? selection.toggle(row) : null" [checked]="selection.isSelected(row)">
|
(change)="$event ? selection.toggle(row) : null" [checked]="selection.isSelected(row)">
|
||||||
<app-avatar *ngIf="row && (row?.displayName || (row.firstName && row.lastName))" class="avatar"
|
<app-avatar *ngIf="row && (row?.displayName || (row.firstName && row.lastName))" class="avatar"
|
||||||
[name]="row.displayName ? row.displayName : (row.firstName + ' '+ row.lastName)" [size]="32">
|
[name]="row.displayName ? row.displayName : (row.firstName + ' '+ row.lastName)"
|
||||||
|
[size]="32">
|
||||||
</app-avatar>
|
</app-avatar>
|
||||||
</mat-checkbox>
|
</mat-checkbox>
|
||||||
</td>
|
</td>
|
||||||
@@ -71,7 +60,6 @@
|
|||||||
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.GRANT.CHANGEDATE' | translate }} </th>
|
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.GRANT.CHANGEDATE' | translate }} </th>
|
||||||
<td class="pointer" mat-cell *matCellDef="let grant">
|
<td class="pointer" mat-cell *matCellDef="let grant">
|
||||||
{{grant.changeDate | timestampToDate | date: 'dd. MMM, HH:mm' }} </td>
|
{{grant.changeDate | timestampToDate | date: 'dd. MMM, HH:mm' }} </td>
|
||||||
|
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container matColumnDef="roleNamesList">
|
<ng-container matColumnDef="roleNamesList">
|
||||||
@@ -84,25 +72,6 @@
|
|||||||
*ngFor="let role of grant.roleKeysList">{{ (role.length>8)? (role | slice:0:8)+'..':(role) }}</span>
|
*ngFor="let role of grant.roleKeysList">{{ (role.length>8)? (role | slice:0:8)+'..':(role) }}</span>
|
||||||
</ng-container>
|
</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="context === UserGrantContext.OWNED_PROJECT">
|
||||||
<ng-container *ngIf="loadedProjectId !== grant.projectId">
|
<ng-container *ngIf="loadedProjectId !== grant.projectId">
|
||||||
<span class="role app-label"
|
<span class="role app-label"
|
||||||
@@ -112,7 +81,8 @@
|
|||||||
<i class="las la-edit"></i>
|
<i class="las la-edit"></i>
|
||||||
</button>
|
</button>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<mat-form-field class="form-field" appearance="outline" *ngIf="loadedProjectId === grant.projectId">
|
<mat-form-field class="form-field" appearance="outline"
|
||||||
|
*ngIf="loadedProjectId === grant.projectId">
|
||||||
<mat-label>{{ 'PROJECT.GRANT.ROLENAMESLIST' | translate }}</mat-label>
|
<mat-label>{{ 'PROJECT.GRANT.ROLENAMESLIST' | translate }}</mat-label>
|
||||||
<mat-select [(ngModel)]="grant.roleKeysList" multiple [disabled]="allowCreate == false"
|
<mat-select [(ngModel)]="grant.roleKeysList" multiple [disabled]="allowCreate == false"
|
||||||
(selectionChange)="updateRoles(grant, $event)">
|
(selectionChange)="updateRoles(grant, $event)">
|
||||||
@@ -145,4 +115,5 @@
|
|||||||
<mat-paginator #paginator [length]="dataSource.totalResult" [pageSize]="50" [length]="dataSource.totalResult"
|
<mat-paginator #paginator [length]="dataSource.totalResult" [pageSize]="50" [length]="dataSource.totalResult"
|
||||||
[pageSizeOptions]="[2, 3, 25, 50, 100, 250]" (page)="changePage($event)">
|
[pageSizeOptions]="[2, 3, 25, 50, 100, 250]" (page)="changePage($event)">
|
||||||
</mat-paginator>
|
</mat-paginator>
|
||||||
</div>
|
</div>
|
||||||
|
</app-refresh-table>
|
@@ -1,31 +1,6 @@
|
|||||||
|
|
||||||
.table-header-row {
|
.add-button {
|
||||||
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;
|
border-radius: .5rem;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-wrapper {
|
.table-wrapper {
|
||||||
|
@@ -176,11 +176,16 @@ export class UserGrantsComponent implements OnInit, AfterViewInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public changePage(event: PageEvent): void {
|
public changePage(event?: PageEvent): void {
|
||||||
this.dataSource.loadGrants(this.context, event.pageIndex, event.pageSize, {
|
this.dataSource.loadGrants(
|
||||||
|
this.context,
|
||||||
|
event?.pageIndex ?? this.paginator.pageIndex,
|
||||||
|
event?.pageSize ?? this.paginator.pageSize,
|
||||||
|
{
|
||||||
projectId: this.projectId,
|
projectId: this.projectId,
|
||||||
grantId: this.grantId,
|
grantId: this.grantId,
|
||||||
userId: this.userId,
|
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 { TimestampToDatePipeModule } from 'src/app/pipes/timestamp-to-date-pipe.module';
|
||||||
|
|
||||||
import { AvatarModule } from '../avatar/avatar.module';
|
import { AvatarModule } from '../avatar/avatar.module';
|
||||||
|
import { RefreshTableModule } from '../refresh-table/refresh-table.module';
|
||||||
import { UserGrantsComponent } from './user-grants.component';
|
import { UserGrantsComponent } from './user-grants.component';
|
||||||
|
|
||||||
|
|
||||||
@@ -41,6 +42,7 @@ import { UserGrantsComponent } from './user-grants.component';
|
|||||||
TranslateModule,
|
TranslateModule,
|
||||||
HasRolePipeModule,
|
HasRolePipeModule,
|
||||||
TimestampToDatePipeModule,
|
TimestampToDatePipeModule,
|
||||||
|
RefreshTableModule,
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
UserGrantsComponent,
|
UserGrantsComponent,
|
||||||
|
@@ -4,8 +4,8 @@
|
|||||||
<div class="people">
|
<div class="people">
|
||||||
<div class="img-list">
|
<div class="img-list">
|
||||||
<ng-container *ngIf="totalResult < 10; else compact">
|
<ng-container *ngIf="totalResult < 10; else compact">
|
||||||
<ng-container *ngFor="let member of membersSubject | async">
|
<ng-container *ngFor="let member of membersSubject | async; index as i">
|
||||||
<div (click)="showDetail()" class="avatar-circle"
|
<div (click)="showDetail()" class="avatar-circle" [ngStyle]="{'z-index': 100 - i}"
|
||||||
matTooltip="{{ member.email }} | {{member.rolesList?.join(' ')}}">
|
matTooltip="{{ member.email }} | {{member.rolesList?.join(' ')}}">
|
||||||
<app-avatar *ngIf="member && (member.displayName || (member.firstName && member.lastName))"
|
<app-avatar *ngIf="member && (member.displayName || (member.firstName && member.lastName))"
|
||||||
class="avatar dontcloseonclick"
|
class="avatar dontcloseonclick"
|
||||||
|
@@ -35,11 +35,9 @@
|
|||||||
height: 32px;
|
height: 32px;
|
||||||
width: 32px;
|
width: 32px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
&:not(:first-child) {
|
-webkit-box-shadow: 2px 0px 7px -1px rgba(33,34,36,.5);
|
||||||
-webkit-box-shadow: -9px 0px 20px -6px rgba(0,0,0,0.75);
|
-moz-box-shadow: 2px 0px 7px -1px rgba(33,34,36,.5);
|
||||||
-moz-box-shadow: -9px 0px 20px -6px rgba(0,0,0,0.75);
|
box-shadow: 2px 0px 7px -1px rgba(33,34,36,.5);
|
||||||
box-shadow: -9px 0px 20px -6px rgba(0,0,0,0.75);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.add-img {
|
.add-img {
|
||||||
|
@@ -4,9 +4,10 @@
|
|||||||
<div class="people">
|
<div class="people">
|
||||||
<div class="img-list">
|
<div class="img-list">
|
||||||
<ng-container *ngIf="totalResult < 10; else compact">
|
<ng-container *ngIf="totalResult < 10; else compact">
|
||||||
<ng-container *ngFor="let member of membersSubject | async">
|
<ng-container *ngFor="let member of membersSubject | async; index as i">
|
||||||
<div (click)="showDetail()" class="avatar-circle"
|
<div (click)="showDetail()" class="avatar-circle"
|
||||||
matTooltip="{{ member.email }} | {{member.rolesList?.join(' ')}}">
|
matTooltip="{{ member.email }} | {{member.rolesList?.join(' ')}}"
|
||||||
|
[ngStyle]="{'z-index': 100 - i}">
|
||||||
<app-avatar *ngIf="member && (member.displayName || (member.firstName && member.lastName))"
|
<app-avatar *ngIf="member && (member.displayName || (member.firstName && member.lastName))"
|
||||||
class="avatar dontcloseonclick"
|
class="avatar dontcloseonclick"
|
||||||
[name]="member.displayName ? member.displayName : (member.firstName + ' '+ member.lastName)"
|
[name]="member.displayName ? member.displayName : (member.firstName + ' '+ member.lastName)"
|
||||||
|
@@ -29,17 +29,15 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
.avatar-img, .avatar-circle {
|
.avatar-circle {
|
||||||
float: left;
|
float: left;
|
||||||
margin: 0 8px 0 -15px;
|
margin: 0 8px 0 -15px;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
width: 32px;
|
width: 32px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
&:not(:first-child) {
|
-webkit-box-shadow: 2px 0px 7px -1px rgba(33,34,36,.5);
|
||||||
-webkit-box-shadow: -9px 0px 20px -6px rgba(0,0,0,0.75);
|
-moz-box-shadow: 2px 0px 7px -1px rgba(33,34,36,.5);
|
||||||
-moz-box-shadow: -9px 0px 20px -6px rgba(0,0,0,0.75);
|
box-shadow: 2px 0px 7px -1px rgba(33,34,36,.5);
|
||||||
box-shadow: -9px 0px 20px -6px rgba(0,0,0,0.75);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.add-img {
|
.add-img {
|
||||||
|
@@ -5,7 +5,7 @@ import { MatTable } from '@angular/material/table';
|
|||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { BehaviorSubject, from, Observable, of } from 'rxjs';
|
import { BehaviorSubject, from, Observable, of } from 'rxjs';
|
||||||
import { catchError, finalize, map } from 'rxjs/operators';
|
import { catchError, finalize, map } from 'rxjs/operators';
|
||||||
import { Org, OrgMember, OrgMemberView, OrgState, User } from 'src/app/proto/generated/management_pb';
|
import { Org, OrgMemberView, OrgState, User } from 'src/app/proto/generated/management_pb';
|
||||||
import { OrgService } from 'src/app/services/org.service';
|
import { OrgService } from 'src/app/services/org.service';
|
||||||
import { ToastService } from 'src/app/services/toast.service';
|
import { ToastService } from 'src/app/services/toast.service';
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@ export class OrgContributorsComponent implements OnInit {
|
|||||||
@Input() public org!: Org.AsObject;
|
@Input() public org!: Org.AsObject;
|
||||||
@Input() public disabled: boolean = false;
|
@Input() public disabled: boolean = false;
|
||||||
@ViewChild(MatPaginator) public paginator!: MatPaginator;
|
@ViewChild(MatPaginator) public paginator!: MatPaginator;
|
||||||
@ViewChild(MatTable) public table!: MatTable<OrgMember.AsObject>;
|
@ViewChild(MatTable) public table!: MatTable<OrgMemberView.AsObject>;
|
||||||
/** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */
|
/** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */
|
||||||
public displayedColumns: string[] = ['select', 'firstname', 'lastname', 'username', 'email', 'roles'];
|
public displayedColumns: string[] = ['select', 'firstname', 'lastname', 'username', 'email', 'roles'];
|
||||||
|
|
||||||
|
@@ -17,6 +17,7 @@ import { ChangesModule } from 'src/app/modules/changes/changes.module';
|
|||||||
import { MetaLayoutModule } from 'src/app/modules/meta-layout/meta-layout.module';
|
import { MetaLayoutModule } from 'src/app/modules/meta-layout/meta-layout.module';
|
||||||
import { ProjectContributorsModule } from 'src/app/modules/project-contributors/project-contributors.module';
|
import { ProjectContributorsModule } from 'src/app/modules/project-contributors/project-contributors.module';
|
||||||
import { ProjectRolesModule } from 'src/app/modules/project-roles/project-roles.module';
|
import { ProjectRolesModule } from 'src/app/modules/project-roles/project-roles.module';
|
||||||
|
import { RefreshTableModule } from 'src/app/modules/refresh-table/refresh-table.module';
|
||||||
import { UserGrantsModule } from 'src/app/modules/user-grants/user-grants.module';
|
import { UserGrantsModule } from 'src/app/modules/user-grants/user-grants.module';
|
||||||
import { WarnDialogModule } from 'src/app/modules/warn-dialog/warn-dialog.module';
|
import { WarnDialogModule } from 'src/app/modules/warn-dialog/warn-dialog.module';
|
||||||
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe.module';
|
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe.module';
|
||||||
@@ -59,6 +60,7 @@ import { ProjectGrantsComponent } from './project-grants/project-grants.componen
|
|||||||
MatProgressSpinnerModule,
|
MatProgressSpinnerModule,
|
||||||
ChangesModule,
|
ChangesModule,
|
||||||
MetaLayoutModule,
|
MetaLayoutModule,
|
||||||
|
RefreshTableModule,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class OwnedProjectDetailModule { }
|
export class OwnedProjectDetailModule { }
|
||||||
|
@@ -10,6 +10,7 @@ import { ProjectService } from 'src/app/services/project.service';
|
|||||||
* (including sorting, pagination, and filtering).
|
* (including sorting, pagination, and filtering).
|
||||||
*/
|
*/
|
||||||
export class ProjectGrantsDataSource extends DataSource<ProjectGrant.AsObject> {
|
export class ProjectGrantsDataSource extends DataSource<ProjectGrant.AsObject> {
|
||||||
|
public totalResult: number = 0;
|
||||||
public grantsSubject: BehaviorSubject<ProjectGrant.AsObject[]> = new BehaviorSubject<ProjectGrant.AsObject[]>([]);
|
public grantsSubject: BehaviorSubject<ProjectGrant.AsObject[]> = new BehaviorSubject<ProjectGrant.AsObject[]>([]);
|
||||||
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||||
public loading$: Observable<boolean> = this.loadingSubject.asObservable();
|
public loading$: Observable<boolean> = this.loadingSubject.asObservable();
|
||||||
@@ -24,6 +25,7 @@ export class ProjectGrantsDataSource extends DataSource<ProjectGrant.AsObject> {
|
|||||||
this.loadingSubject.next(true);
|
this.loadingSubject.next(true);
|
||||||
from(this.projectService.SearchProjectGrants(projectId, pageSize, offset)).pipe(
|
from(this.projectService.SearchProjectGrants(projectId, pageSize, offset)).pipe(
|
||||||
map(resp => {
|
map(resp => {
|
||||||
|
this.totalResult = resp.toObject().totalResult;
|
||||||
return resp.toObject().resultList;
|
return resp.toObject().resultList;
|
||||||
}),
|
}),
|
||||||
catchError(() => of([])),
|
catchError(() => of([])),
|
||||||
|
@@ -1,30 +1,21 @@
|
|||||||
<div class="table-header-row" *ngIf="projectId">
|
<app-refresh-table *ngIf="projectId" (refreshed)="loadGrantsPage()" [dataSize]="dataSource.totalResult"
|
||||||
<div class="col">
|
[selection]="selection">
|
||||||
<ng-container *ngIf="!selection.hasValue()">
|
<ng-template appHasRole [appHasRole]="['project.grant.member.delete:'+projectId, 'project.grant.member.delete']"
|
||||||
<span class="desc">{{'ORG_DETAIL.TABLE.TOTAL' | translate}}</span>
|
actions>
|
||||||
<span class="count">{{dataSource.grantsSubject.value.length}}</span>
|
<button (click)="deleteSelectedGrants()" [disabled]="disabled" mat-icon-button *ngIf="selection.hasValue()"
|
||||||
</ng-container>
|
color="warn">
|
||||||
<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.grant.member.delete:'+projectId, 'project.grant.member.delete']">
|
|
||||||
<button (click)="deleteSelectedGrants()" [disabled]="disabled" class="icon-button" mat-icon-button
|
|
||||||
*ngIf="selection.hasValue()" color="warn">
|
|
||||||
<i class="las la-trash"></i>
|
<i class="las la-trash"></i>
|
||||||
</button>
|
</button>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
<ng-template appHasRole [appHasRole]="['project.grant.member.write:'+projectId,'project.grant.member.write']">
|
<ng-template appHasRole [appHasRole]="['project.grant.member.write:'+projectId,'project.grant.member.write']"
|
||||||
<a [disabled]="disabled" color="primary" class="add-button" color="primary" mat-raised-button
|
actions>
|
||||||
|
<a [disabled]="disabled" color="primary" class="rounded-button" color="primary" mat-raised-button
|
||||||
[routerLink]="[ '/projects', projectId, 'grants', 'create']">
|
[routerLink]="[ '/projects', projectId, 'grants', 'create']">
|
||||||
<mat-icon class="icon">add</mat-icon>{{ 'ACTIONS.NEW' | translate }}
|
<mat-icon class="icon">add</mat-icon>{{ 'ACTIONS.NEW' | translate }}
|
||||||
</a>
|
</a>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="table-wrapper">
|
<div class="table-wrapper">
|
||||||
<div class="spinner-container" *ngIf="dataSource.loading$ | async">
|
<div class="spinner-container" *ngIf="dataSource.loading$ | async">
|
||||||
<mat-spinner diameter="50"></mat-spinner>
|
<mat-spinner diameter="50"></mat-spinner>
|
||||||
</div>
|
</div>
|
||||||
@@ -86,6 +77,8 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<mat-paginator #paginator [pageSize]="50" [pageSizeOptions]="[25, 50, 100, 250]">
|
<mat-paginator #paginator [pageSize]="50" [pageSizeOptions]="[25, 50, 100, 250]"
|
||||||
|
[length]="dataSource.totalResult" (page)="loadGrantsPage($event.pageIndex, $event.pageSize)">
|
||||||
</mat-paginator>
|
</mat-paginator>
|
||||||
</div>
|
</div>
|
||||||
|
</app-refresh-table>
|
@@ -1,30 +1,5 @@
|
|||||||
.table-header-row {
|
.rounded-button {
|
||||||
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;
|
border-radius: .5rem;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-wrapper {
|
.table-wrapper {
|
||||||
|
@@ -52,11 +52,11 @@ export class ProjectGrantsComponent implements OnInit, AfterViewInit {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private loadGrantsPage(): void {
|
public loadGrantsPage(pageIndex?: number, pageSize?: number): void {
|
||||||
this.dataSource.loadGrants(
|
this.dataSource.loadGrants(
|
||||||
this.projectId,
|
this.projectId,
|
||||||
this.paginator.pageIndex,
|
pageIndex ?? this.paginator.pageIndex,
|
||||||
this.paginator.pageSize,
|
pageSize ?? this.paginator.pageSize,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -29,7 +29,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<button [ngClass]="{ selected: selection.isSelected(item)}" (click)="selection.toggle(item)" class="edit-button"
|
<button [ngClass]="{ selected: selection.isSelected(item)}" (click)="selection.toggle(item)" class="edit-button"
|
||||||
mat-icon-button>
|
mat-icon-button>
|
||||||
<mat-icon>push_pin_outline</mat-icon>
|
<mat-icon>push_pin</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<button [ngClass]="{ selected: selection.isSelected(item)}" (click)="selection.toggle(item)" class="edit-button"
|
<button [ngClass]="{ selected: selection.isSelected(item)}" (click)="selection.toggle(item)" class="edit-button"
|
||||||
mat-icon-button>
|
mat-icon-button>
|
||||||
<mat-icon>push_pin_outline</mat-icon>
|
<mat-icon>push_pin</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@@ -126,7 +126,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</app-card>
|
</app-card>
|
||||||
|
|
||||||
<app-auth-user-mfa *ngIf="user"></app-auth-user-mfa>
|
<app-auth-user-mfa *ngIf="user" #mfaComponent></app-auth-user-mfa>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngIf="user" class="side" metainfo>
|
<div *ngIf="user" class="side" metainfo>
|
||||||
|
@@ -1,19 +1,37 @@
|
|||||||
<app-card title="{{'USER.MFA.TITLE' | translate}}" description="{{'USER.MFA.DESCRIPTION' | translate}}">
|
<app-card title="{{'USER.MFA.TITLE' | translate}}" description="{{'USER.MFA.DESCRIPTION' | translate}}">
|
||||||
<div class="col">
|
<app-refresh-table (refreshed)="getOTP()" [dataSize]="dataSource?.data?.length">
|
||||||
<div class="row" *ngFor="let mfa of mfaSubject | async">
|
<table class="background-style" mat-table [dataSource]="dataSource">
|
||||||
<span>{{'USER.MFA.TYPE.'+ mfa.type | translate}}</span>
|
<ng-container matColumnDef="type">
|
||||||
<i matTooltip="{{'USER.MFA.STATE.'+ mfa.state | translate}}" *ngIf="mfa.state === MFAState.MFASTATE_READY"
|
<th mat-header-cell *matHeaderCellDef> {{ 'USER.MFA.TABLETYPE' | translate }} </th>
|
||||||
|
<td mat-cell *matCellDef="let mfa"> {{'USER.MFA.TYPE.'+ mfa.type | translate}} </td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="state">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> {{ 'USER.MFA.TABLESTATE' | translate }} </th>
|
||||||
|
<td mat-cell *matCellDef="let mfa"><span class="centered">
|
||||||
|
{{'USER.MFA.STATE.'+ mfa.state | translate}}
|
||||||
|
<i matTooltip="verified" *ngIf="mfa.state === MFAState.MFASTATE_READY"
|
||||||
class="verified las la-check-circle"></i>
|
class="verified las la-check-circle"></i>
|
||||||
<i matTooltip="{{'USER.MFA.STATE.'+ mfa.state | translate}}"
|
</span>
|
||||||
*ngIf="mfa.state === MFAState.MFASTATE_NOT_READY || mfa.state === MFAState.MFASTATE_REMOVED"
|
</td>
|
||||||
class="primary las la-ban"></i>
|
</ng-container>
|
||||||
<button mat-icon-button (click)="deleteMFA(mfa.type)" color="warn"
|
|
||||||
matTooltip="{{'ACTIONS.DELETE' | translate}}">
|
<ng-container matColumnDef="actions">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> {{ 'USER.MFA.TABLEACTIONS' | translate }} </th>
|
||||||
|
<td mat-cell *matCellDef="let mfa">
|
||||||
|
<button matTooltip="{{'ACTIONS.REMOVE' | translate}}" color="warn" mat-icon-button
|
||||||
|
(click)="deleteMFA(mfa.type)">
|
||||||
<i class="las la-trash"></i>
|
<i class="las la-trash"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</td>
|
||||||
<p class="row" *ngIf="error">{{error}}</p>
|
</ng-container>
|
||||||
</div>
|
|
||||||
|
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||||
|
<tr class="data-row" mat-row *matRowDef="let row; columns: displayedColumns;"
|
||||||
|
[routerLink]="['/users', row.id]">
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</app-refresh-table>
|
||||||
<div class="add-row">
|
<div class="add-row">
|
||||||
<button *ngIf="otpAvailable" (click)="addOTP()" mat-stroked-button color="primary"
|
<button *ngIf="otpAvailable" (click)="addOTP()" mat-stroked-button color="primary"
|
||||||
matTooltip="{{'ACTIONS.NEW' | translate}}">
|
matTooltip="{{'ACTIONS.NEW' | translate}}">
|
||||||
|
@@ -1,22 +1,4 @@
|
|||||||
|
|
||||||
.col {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
border-bottom: 1px solid #ffffff20;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
|
|
||||||
.row {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
padding: .5rem 0;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
span {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.add-row {
|
.add-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
margin: -.5rem;
|
margin: -.5rem;
|
||||||
@@ -24,9 +6,35 @@
|
|||||||
button {
|
button {
|
||||||
border-radius: .5rem;
|
border-radius: .5rem;
|
||||||
margin: .5rem;
|
margin: .5rem;
|
||||||
|
margin-top: 1rem;
|
||||||
|
|
||||||
mat-icon {
|
mat-icon {
|
||||||
margin-right: .5rem;
|
margin-right: .5rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.centered {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
i {
|
||||||
|
margin-left: 1rem;
|
||||||
|
color: #5282c1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
td, th {
|
||||||
|
&:first-child {
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,5 +1,7 @@
|
|||||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
|
||||||
import { MatDialog } from '@angular/material/dialog';
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
|
import { MatSort } from '@angular/material/sort';
|
||||||
|
import { MatTable, MatTableDataSource } from '@angular/material/table';
|
||||||
import { BehaviorSubject, Observable } from 'rxjs';
|
import { BehaviorSubject, Observable } from 'rxjs';
|
||||||
import { MfaOtpResponse, MFAState, MfaType, MultiFactor } from 'src/app/proto/generated/auth_pb';
|
import { MfaOtpResponse, MFAState, MfaType, MultiFactor } from 'src/app/proto/generated/auth_pb';
|
||||||
import { AuthUserService } from 'src/app/services/auth-user.service';
|
import { AuthUserService } from 'src/app/services/auth-user.service';
|
||||||
@@ -13,28 +15,31 @@ import { DialogOtpComponent } from '../dialog-otp/dialog-otp.component';
|
|||||||
styleUrls: ['./auth-user-mfa.component.scss'],
|
styleUrls: ['./auth-user-mfa.component.scss'],
|
||||||
})
|
})
|
||||||
export class AuthUserMfaComponent implements OnInit, OnDestroy {
|
export class AuthUserMfaComponent implements OnInit, OnDestroy {
|
||||||
public mfaSubject: BehaviorSubject<MultiFactor.AsObject[]> = new BehaviorSubject<MultiFactor.AsObject[]>([]);
|
public displayedColumns: string[] = ['type', 'state', 'actions'];
|
||||||
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||||
public loading$: Observable<boolean> = this.loadingSubject.asObservable();
|
public loading$: Observable<boolean> = this.loadingSubject.asObservable();
|
||||||
|
|
||||||
|
@ViewChild(MatTable) public table!: MatTable<MultiFactor.AsObject>;
|
||||||
|
@ViewChild(MatSort) public sort!: MatSort;
|
||||||
|
public dataSource!: MatTableDataSource<MultiFactor.AsObject>;
|
||||||
|
|
||||||
public MfaType: any = MfaType;
|
public MfaType: any = MfaType;
|
||||||
public MFAState: any = MFAState;
|
public MFAState: any = MFAState;
|
||||||
|
|
||||||
public error: string = '';
|
public error: string = '';
|
||||||
public otpAvailable: boolean = false;
|
public otpAvailable: boolean = false;
|
||||||
constructor(private userService: AuthUserService, private toast: ToastService, private dialog: MatDialog) { }
|
constructor(private service: AuthUserService, private toast: ToastService, private dialog: MatDialog) { }
|
||||||
|
|
||||||
public ngOnInit(): void {
|
public ngOnInit(): void {
|
||||||
this.getOTP();
|
this.getOTP();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ngOnDestroy(): void {
|
public ngOnDestroy(): void {
|
||||||
this.mfaSubject.complete();
|
|
||||||
this.loadingSubject.complete();
|
this.loadingSubject.complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
public addOTP(): void {
|
public addOTP(): void {
|
||||||
this.userService.AddMfaOTP().then((otpresp) => {
|
this.service.AddMfaOTP().then((otpresp) => {
|
||||||
const otp: MfaOtpResponse.AsObject = otpresp.toObject();
|
const otp: MfaOtpResponse.AsObject = otpresp.toObject();
|
||||||
const dialogRef = this.dialog.open(DialogOtpComponent, {
|
const dialogRef = this.dialog.open(DialogOtpComponent, {
|
||||||
data: otp.url,
|
data: otp.url,
|
||||||
@@ -43,7 +48,7 @@ export class AuthUserMfaComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
dialogRef.afterClosed().subscribe((code) => {
|
dialogRef.afterClosed().subscribe((code) => {
|
||||||
if (code) {
|
if (code) {
|
||||||
this.userService.VerifyMfaOTP(code).then((res) => {
|
(this.service as AuthUserService).VerifyMfaOTP(code).then(() => {
|
||||||
// TODO: show state
|
// TODO: show state
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -54,28 +59,28 @@ export class AuthUserMfaComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public getOTP(): void {
|
public getOTP(): void {
|
||||||
this.userService.GetMyMfas().then(mfas => {
|
this.service.GetMyMfas().then(mfas => {
|
||||||
this.mfaSubject.next(mfas.toObject().mfasList);
|
console.log(mfas.toObject().mfasList);
|
||||||
|
this.dataSource = new MatTableDataSource(mfas.toObject().mfasList);
|
||||||
|
this.dataSource.sort = this.sort;
|
||||||
|
|
||||||
const index = mfas.toObject().mfasList.findIndex(mfa => mfa.type === MfaType.MFATYPE_OTP);
|
const index = mfas.toObject().mfasList.findIndex(mfa => mfa.type === MfaType.MFATYPE_OTP);
|
||||||
if (index === -1) {
|
if (index === -1) {
|
||||||
this.otpAvailable = true;
|
this.otpAvailable = true;
|
||||||
}
|
}
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
console.error(error);
|
|
||||||
this.error = error.message;
|
this.error = error.message;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public deleteMFA(type: MfaType): void {
|
public deleteMFA(type: MfaType): void {
|
||||||
if (type === MfaType.MFATYPE_OTP) {
|
if (type === MfaType.MFATYPE_OTP) {
|
||||||
this.userService.RemoveMfaOTP().then(() => {
|
this.service.RemoveMfaOTP().then(() => {
|
||||||
this.toast.showInfo('USER.TOAST.OTPREMOVED', true);
|
this.toast.showInfo('USER.TOAST.OTPREMOVED', true);
|
||||||
|
|
||||||
const index = this.mfaSubject.value.findIndex(mfa => mfa.type === type);
|
const index = this.dataSource.data.findIndex(mfa => mfa.type === type);
|
||||||
if (index > -1) {
|
if (index > -1) {
|
||||||
const newValues = this.mfaSubject.value;
|
this.dataSource.data.splice(index, 1);
|
||||||
newValues.splice(index, 1);
|
|
||||||
this.mfaSubject.next(newValues);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
|
@@ -17,6 +17,7 @@ import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
|
|||||||
import { CardModule } from 'src/app/modules/card/card.module';
|
import { CardModule } from 'src/app/modules/card/card.module';
|
||||||
import { ChangesModule } from 'src/app/modules/changes/changes.module';
|
import { ChangesModule } from 'src/app/modules/changes/changes.module';
|
||||||
import { MetaLayoutModule } from 'src/app/modules/meta-layout/meta-layout.module';
|
import { MetaLayoutModule } from 'src/app/modules/meta-layout/meta-layout.module';
|
||||||
|
import { RefreshTableModule } from 'src/app/modules/refresh-table/refresh-table.module';
|
||||||
import { SharedModule } from 'src/app/modules/shared/shared.module';
|
import { SharedModule } from 'src/app/modules/shared/shared.module';
|
||||||
import { UserGrantsModule } from 'src/app/modules/user-grants/user-grants.module';
|
import { UserGrantsModule } from 'src/app/modules/user-grants/user-grants.module';
|
||||||
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe.module';
|
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe.module';
|
||||||
@@ -68,6 +69,7 @@ import { UserMfaComponent } from './user-detail/user-mfa/user-mfa.component';
|
|||||||
MatTableModule,
|
MatTableModule,
|
||||||
MatPaginatorModule,
|
MatPaginatorModule,
|
||||||
SharedModule,
|
SharedModule,
|
||||||
|
RefreshTableModule,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class UserDetailModule { }
|
export class UserDetailModule { }
|
||||||
|
@@ -1,12 +1,28 @@
|
|||||||
<app-card title="{{'USER.MFA.TITLE' | translate}}" description="{{'USER.MFA.DESCRIPTION' | translate}}"
|
<app-card title="{{'USER.MFA.TITLE' | translate}}" description="{{'USER.MFA.DESCRIPTION' | translate}}">
|
||||||
*ngIf="mfaSubject && (mfaSubject | async)?.length > 0">
|
<app-refresh-table (refreshed)="getOTP()" [dataSize]="dataSource?.data?.length">
|
||||||
<div class="col">
|
<table class="background-style" mat-table [dataSource]="dataSource">
|
||||||
<div class="row" *ngFor="let mfa of mfaSubject | async">
|
<ng-container matColumnDef="type">
|
||||||
<span>{{'USER.MFA.TYPE.'+ mfa.type | translate}}</span>
|
<th mat-header-cell *matHeaderCellDef> {{ 'USER.MFA.TABLETYPE' | translate }} </th>
|
||||||
<span>{{'USER.MFA.STATE.'+ mfa.state | translate}}</span>
|
<td mat-cell *matCellDef="let mfa"> {{'USER.MFA.TYPE.'+ mfa.type | translate}} </td>
|
||||||
</div>
|
</ng-container>
|
||||||
<p class="row" *ngIf="error">{{error}}</p>
|
|
||||||
</div>
|
<ng-container matColumnDef="state">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> {{ 'USER.MFA.TABLESTATE' | translate }} </th>
|
||||||
|
<td mat-cell *matCellDef="let mfa">
|
||||||
|
<span class="centered">
|
||||||
|
{{'USER.MFA.STATE.'+ mfa.state | translate}}
|
||||||
|
<i matTooltip="verified" *ngIf="mfa.state === MFAState.MFASTATE_READY"
|
||||||
|
class="verified las la-check-circle"></i>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||||
|
<tr class="data-row" mat-row *matRowDef="let row; columns: displayedColumns;"
|
||||||
|
[routerLink]="['/users', row.id]">
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</app-refresh-table>
|
||||||
<div class="table-wrapper">
|
<div class="table-wrapper">
|
||||||
<div class="spinner-container" *ngIf="loading$ | async">
|
<div class="spinner-container" *ngIf="loading$ | async">
|
||||||
<mat-spinner diameter="50"></mat-spinner>
|
<mat-spinner diameter="50"></mat-spinner>
|
||||||
|
@@ -16,3 +16,28 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.centered {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
i {
|
||||||
|
margin-left: 1rem;
|
||||||
|
color: #5282c1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
td, th {
|
||||||
|
&:first-child {
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,4 +1,6 @@
|
|||||||
import { Component, Input, OnDestroy, OnInit } from '@angular/core';
|
import { Component, Input, OnDestroy, OnInit, ViewChild } from '@angular/core';
|
||||||
|
import { MatSort } from '@angular/material/sort';
|
||||||
|
import { MatTable, MatTableDataSource } from '@angular/material/table';
|
||||||
import { BehaviorSubject, Observable } from 'rxjs';
|
import { BehaviorSubject, Observable } from 'rxjs';
|
||||||
import { MFAState, MfaType, MultiFactor, UserView } from 'src/app/proto/generated/management_pb';
|
import { MFAState, MfaType, MultiFactor, UserView } from 'src/app/proto/generated/management_pb';
|
||||||
import { MgmtUserService } from 'src/app/services/mgmt-user.service';
|
import { MgmtUserService } from 'src/app/services/mgmt-user.service';
|
||||||
@@ -15,11 +17,16 @@ export interface MFAItem {
|
|||||||
styleUrls: ['./user-mfa.component.scss'],
|
styleUrls: ['./user-mfa.component.scss'],
|
||||||
})
|
})
|
||||||
export class UserMfaComponent implements OnInit, OnDestroy {
|
export class UserMfaComponent implements OnInit, OnDestroy {
|
||||||
|
public displayedColumns: string[] = ['type', 'state'];
|
||||||
@Input() private user!: UserView.AsObject;
|
@Input() private user!: UserView.AsObject;
|
||||||
public mfaSubject: BehaviorSubject<MultiFactor.AsObject[]> = new BehaviorSubject<MultiFactor.AsObject[]>([]);
|
public mfaSubject: BehaviorSubject<MultiFactor.AsObject[]> = new BehaviorSubject<MultiFactor.AsObject[]>([]);
|
||||||
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||||
public loading$: Observable<boolean> = this.loadingSubject.asObservable();
|
public loading$: Observable<boolean> = this.loadingSubject.asObservable();
|
||||||
|
|
||||||
|
@ViewChild(MatTable) public table!: MatTable<MultiFactor.AsObject>;
|
||||||
|
@ViewChild(MatSort) public sort!: MatSort;
|
||||||
|
public dataSource!: MatTableDataSource<MultiFactor.AsObject>;
|
||||||
|
|
||||||
public MfaType: any = MfaType;
|
public MfaType: any = MfaType;
|
||||||
public MFAState: any = MFAState;
|
public MFAState: any = MFAState;
|
||||||
|
|
||||||
@@ -37,10 +44,10 @@ export class UserMfaComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
public getOTP(): void {
|
public getOTP(): void {
|
||||||
this.mgmtUserService.getUserMfas(this.user.id).then(mfas => {
|
this.mgmtUserService.getUserMfas(this.user.id).then(mfas => {
|
||||||
this.mfaSubject.next(mfas.toObject().mfasList);
|
console.log(mfas.toObject().mfasList);
|
||||||
this.error = '';
|
this.dataSource = new MatTableDataSource(mfas.toObject().mfasList);
|
||||||
|
this.dataSource.sort = this.sort;
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
console.error(error);
|
|
||||||
this.error = error.message;
|
this.error = error.message;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@@ -2,19 +2,8 @@
|
|||||||
<h1>{{ 'USER.PAGES.LIST' | translate }}</h1>
|
<h1>{{ 'USER.PAGES.LIST' | translate }}</h1>
|
||||||
<p class="sub">{{ 'USER.PAGES.DESCRIPTION' | translate }}</p>
|
<p class="sub">{{ 'USER.PAGES.DESCRIPTION' | translate }}</p>
|
||||||
|
|
||||||
<div class="table-header-row">
|
<app-refresh-table (refreshed)="refreshPage()" [dataSize]="dataSource.data.length">
|
||||||
<div class="col">
|
<ng-template appHasRole [appHasRole]="['user.write']" actions>
|
||||||
<ng-container *ngIf="!selection.hasValue()">
|
|
||||||
<span class="desc">{{'ORG_DETAIL.TABLE.TOTAL' | translate}}</span>
|
|
||||||
<span class="count">{{dataSource?.data?.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]="['user.write']">
|
|
||||||
<button (click)="deactivateSelectedUsers()" matTooltip="{{'ORG_DETAIL.TABLE.DEACTIVATE' | translate}}"
|
<button (click)="deactivateSelectedUsers()" matTooltip="{{'ORG_DETAIL.TABLE.DEACTIVATE' | translate}}"
|
||||||
class="icon-button" mat-icon-button *ngIf="selection.hasValue()">
|
class="icon-button" mat-icon-button *ngIf="selection.hasValue()">
|
||||||
<mat-icon svgIcon="mdi_account_cancel"></mat-icon>
|
<mat-icon svgIcon="mdi_account_cancel"></mat-icon>
|
||||||
@@ -27,7 +16,7 @@
|
|||||||
<mat-icon class="icon">add</mat-icon>{{ 'ACTIONS.NEW' | translate }}
|
<mat-icon class="icon">add</mat-icon>{{ 'ACTIONS.NEW' | translate }}
|
||||||
</a>
|
</a>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</div>
|
|
||||||
<div class="table-wrapper">
|
<div class="table-wrapper">
|
||||||
<div class="spinner-container" *ngIf="loading$ | async">
|
<div class="spinner-container" *ngIf="loading$ | async">
|
||||||
<mat-spinner diameter="50"></mat-spinner>
|
<mat-spinner diameter="50"></mat-spinner>
|
||||||
@@ -80,7 +69,8 @@
|
|||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
</table>
|
</table>
|
||||||
<mat-paginator class="background-style" [length]="userResult?.totalResult || 0" [pageSize]="10"
|
<mat-paginator #paginator class="background-style" [length]="userResult?.totalResult || 0" [pageSize]="10"
|
||||||
[pageSizeOptions]="[5, 10, 20]" (page)="changePage($event)"></mat-paginator>
|
[pageSizeOptions]="[5, 10, 20]" (page)="changePage($event)"></mat-paginator>
|
||||||
</div>
|
</div>
|
||||||
|
</app-refresh-table>
|
||||||
</div>
|
</div>
|
@@ -7,34 +7,8 @@ h1 {
|
|||||||
margin-bottom: 2rem;
|
margin-bottom: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-header-row {
|
.add-button {
|
||||||
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;
|
border-radius: .5rem;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-wrapper {
|
.table-wrapper {
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import { SelectionModel } from '@angular/cdk/collections';
|
import { SelectionModel } from '@angular/cdk/collections';
|
||||||
import { Component, EventEmitter, OnDestroy, Output } from '@angular/core';
|
import { Component, EventEmitter, OnDestroy, Output, ViewChild } from '@angular/core';
|
||||||
import { PageEvent } from '@angular/material/paginator';
|
import { MatPaginator, PageEvent } from '@angular/material/paginator';
|
||||||
import { MatTableDataSource } from '@angular/material/table';
|
import { MatTableDataSource } from '@angular/material/table';
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
@@ -15,6 +15,7 @@ import { ToastService } from 'src/app/services/toast.service';
|
|||||||
styleUrls: ['./user-list.component.scss'],
|
styleUrls: ['./user-list.component.scss'],
|
||||||
})
|
})
|
||||||
export class UserListComponent implements OnDestroy {
|
export class UserListComponent implements OnDestroy {
|
||||||
|
@ViewChild(MatPaginator) public paginator!: MatPaginator;
|
||||||
public dataSource: MatTableDataSource<User.AsObject> = new MatTableDataSource<User.AsObject>();
|
public dataSource: MatTableDataSource<User.AsObject> = new MatTableDataSource<User.AsObject>();
|
||||||
public userResult!: UserSearchResponse.AsObject;
|
public userResult!: UserSearchResponse.AsObject;
|
||||||
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||||
@@ -83,4 +84,8 @@ export class UserListComponent implements OnDestroy {
|
|||||||
this.loadingSubject.next(false);
|
this.loadingSubject.next(false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public refreshPage(): void {
|
||||||
|
this.getData(this.paginator.pageSize, this.paginator.pageIndex * this.paginator.pageSize);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -13,13 +13,13 @@ import { TranslateModule } from '@ngx-translate/core';
|
|||||||
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
|
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
|
||||||
import { AvatarModule } from 'src/app/modules/avatar/avatar.module';
|
import { AvatarModule } from 'src/app/modules/avatar/avatar.module';
|
||||||
import { CardModule } from 'src/app/modules/card/card.module';
|
import { CardModule } from 'src/app/modules/card/card.module';
|
||||||
|
import { RefreshTableModule } from 'src/app/modules/refresh-table/refresh-table.module';
|
||||||
import { SharedModule } from 'src/app/modules/shared/shared.module';
|
import { SharedModule } from 'src/app/modules/shared/shared.module';
|
||||||
|
|
||||||
import { UserListRoutingModule } from './user-list-routing.module';
|
import { UserListRoutingModule } from './user-list-routing.module';
|
||||||
import { UserListComponent } from './user-list.component';
|
import { UserListComponent } from './user-list.component';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
UserListComponent,
|
UserListComponent,
|
||||||
@@ -41,6 +41,7 @@ import { UserListComponent } from './user-list.component';
|
|||||||
MatTooltipModule,
|
MatTooltipModule,
|
||||||
TranslateModule,
|
TranslateModule,
|
||||||
SharedModule,
|
SharedModule,
|
||||||
|
RefreshTableModule,
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
UserListComponent,
|
UserListComponent,
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
|
import { PlatformLocation } from '@angular/common';
|
||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { PlatformLocation } from '@angular/common';
|
|
||||||
|
|
||||||
import { AdminServicePromiseClient } from '../proto/generated/admin_grpc_web_pb';
|
import { AdminServicePromiseClient } from '../proto/generated/admin_grpc_web_pb';
|
||||||
import { AuthServicePromiseClient } from '../proto/generated/auth_grpc_web_pb';
|
import { AuthServicePromiseClient } from '../proto/generated/auth_grpc_web_pb';
|
||||||
@@ -28,7 +28,10 @@ export class GrpcService {
|
|||||||
public async loadAppEnvironment(): Promise<any> {
|
public async loadAppEnvironment(): Promise<any> {
|
||||||
return this.http.get('./assets/environment.json')
|
return this.http.get('./assets/environment.json')
|
||||||
.toPromise().then((data: any) => {
|
.toPromise().then((data: any) => {
|
||||||
|
console.log('init grpc');
|
||||||
|
|
||||||
if (data && data.authServiceUrl && data.mgmtServiceUrl && data.issuer) {
|
if (data && data.authServiceUrl && data.mgmtServiceUrl && data.issuer) {
|
||||||
|
console.log('init grpc promiseclients');
|
||||||
this.auth = new AuthServicePromiseClient(data.authServiceUrl);
|
this.auth = new AuthServicePromiseClient(data.authServiceUrl);
|
||||||
this.mgmt = new ManagementServicePromiseClient(data.mgmtServiceUrl);
|
this.mgmt = new ManagementServicePromiseClient(data.mgmtServiceUrl);
|
||||||
this.admin = new AdminServicePromiseClient(data.adminServiceUrl);
|
this.admin = new AdminServicePromiseClient(data.adminServiceUrl);
|
||||||
|
@@ -217,23 +217,6 @@ export class MgmtUserService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// public async CreateUserGrant(
|
|
||||||
// projectId: string,
|
|
||||||
// userId: string,
|
|
||||||
// roleNamesList: string[],
|
|
||||||
// ): Promise<UserGrant> {
|
|
||||||
// const req = new UserGrantCreate();
|
|
||||||
// req.setProjectId(projectId);
|
|
||||||
// req.setUserId(userId);
|
|
||||||
// req.setRoleKeysList(roleNamesList);
|
|
||||||
|
|
||||||
// return await this.request(
|
|
||||||
// c => c.createUserGrant,
|
|
||||||
// req,
|
|
||||||
// f => f,
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
public async CreateProjectUserGrant(
|
public async CreateProjectUserGrant(
|
||||||
projectId: string,
|
projectId: string,
|
||||||
userId: string,
|
userId: string,
|
||||||
|
@@ -50,7 +50,8 @@
|
|||||||
"FINISH":"Abschliessen",
|
"FINISH":"Abschliessen",
|
||||||
"CHANGE":"Ändern",
|
"CHANGE":"Ändern",
|
||||||
"REACTIVATE":"Aktivieren",
|
"REACTIVATE":"Aktivieren",
|
||||||
"DEACTIVATE":"Deaktivieren"
|
"DEACTIVATE":"Deaktivieren",
|
||||||
|
"REFRESH":"Aktualisieren"
|
||||||
},
|
},
|
||||||
"ERRORS": {
|
"ERRORS": {
|
||||||
"REQUIRED": "Bitte fülle alle benötigten Felder aus!"
|
"REQUIRED": "Bitte fülle alle benötigten Felder aus!"
|
||||||
@@ -74,6 +75,9 @@
|
|||||||
"DEACTIVATE":"Deaktivieren"
|
"DEACTIVATE":"Deaktivieren"
|
||||||
},
|
},
|
||||||
"MFA": {
|
"MFA": {
|
||||||
|
"TABLETYPE":"Typ",
|
||||||
|
"TABLESTATE":"Status",
|
||||||
|
"TABLEACTIONS":"Aktionen",
|
||||||
"TITLE": "Multifaktor-Authentifizierung",
|
"TITLE": "Multifaktor-Authentifizierung",
|
||||||
"DESCRIPTION": "Füge einen zusätzlichen Faktor hinzu, um deinen Account optimal zu schützen.",
|
"DESCRIPTION": "Füge einen zusätzlichen Faktor hinzu, um deinen Account optimal zu schützen.",
|
||||||
"MANAGE_DESCRIPTION": "Verwalte die Multifaktor Merkmale deiner Benutzer.",
|
"MANAGE_DESCRIPTION": "Verwalte die Multifaktor Merkmale deiner Benutzer.",
|
||||||
|
@@ -49,8 +49,9 @@
|
|||||||
"VERIFY":"Verify",
|
"VERIFY":"Verify",
|
||||||
"FINISH":"Finish",
|
"FINISH":"Finish",
|
||||||
"CHANGE":"Change",
|
"CHANGE":"Change",
|
||||||
"REACTIVATE":"Aktivieren",
|
"REACTIVATE":"Reactivate",
|
||||||
"DEACTIVATE":"Deaktivieren"
|
"DEACTIVATE":"Deactivate",
|
||||||
|
"REFRESH":"Refresh"
|
||||||
},
|
},
|
||||||
"ERRORS": {
|
"ERRORS": {
|
||||||
"REQUIRED": "Some required fields are missing!"
|
"REQUIRED": "Some required fields are missing!"
|
||||||
@@ -74,6 +75,9 @@
|
|||||||
"DEACTIVATE":"Deactivate"
|
"DEACTIVATE":"Deactivate"
|
||||||
},
|
},
|
||||||
"MFA": {
|
"MFA": {
|
||||||
|
"TABLETYPE":"Type",
|
||||||
|
"TABLESTATE":"Status",
|
||||||
|
"TABLEACTIONS":"Actions",
|
||||||
"TITLE": "Multifactor Authentication",
|
"TITLE": "Multifactor Authentication",
|
||||||
"DESCRIPTION": "Add a second factor to ensure optimal security for your account.",
|
"DESCRIPTION": "Add a second factor to ensure optimal security for your account.",
|
||||||
"MANAGE_DESCRIPTION": "Manage the second factor methods of your users.",
|
"MANAGE_DESCRIPTION": "Manage the second factor methods of your users.",
|
||||||
|
@@ -9,7 +9,7 @@
|
|||||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||||
<link href="https://fonts.googleapis.com/css?family=Manjari|Roboto+Slab&display=swap" rel="stylesheet" />
|
<link href="https://fonts.googleapis.com/css?family=Manjari|Roboto+Slab&display=swap" rel="stylesheet" />
|
||||||
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500&display=swap" rel="stylesheet">
|
||||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/icon?family=Material+Icons|Material+Icons+Outlined" rel="stylesheet">
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Lato&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=Lato&display=swap" rel="stylesheet">
|
||||||
<link rel="stylesheet"
|
<link rel="stylesheet"
|
||||||
href="https://maxst.icons8.com/vue-static/landings/line-awesome/line-awesome/1.3.0/css/line-awesome.min.css">
|
href="https://maxst.icons8.com/vue-static/landings/line-awesome/line-awesome/1.3.0/css/line-awesome.min.css">
|
||||||
|
@@ -91,16 +91,49 @@ $caos-light-brand: (
|
|||||||
A700: white,
|
A700: white,
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$caos-accent-color: (
|
||||||
|
50: #ebf4f2,
|
||||||
|
100: #cce3de,
|
||||||
|
200: #abd1c9,
|
||||||
|
300: #89bfb3,
|
||||||
|
400: #6fb1a2,
|
||||||
|
500: #56a392,
|
||||||
|
600: #4f9b8a,
|
||||||
|
700: #45917f,
|
||||||
|
800: #3c8875,
|
||||||
|
900: #2b7763,
|
||||||
|
A100: #beffed,
|
||||||
|
A200: #8bffde,
|
||||||
|
A400: #58ffd0,
|
||||||
|
A700: #3effc9,
|
||||||
|
contrast: (
|
||||||
|
50: $black-87-opacity,
|
||||||
|
100: $black-87-opacity,
|
||||||
|
200: $black-87-opacity,
|
||||||
|
300: $black-87-opacity,
|
||||||
|
400: $black-87-opacity,
|
||||||
|
500: white,
|
||||||
|
600: white,
|
||||||
|
700: white,
|
||||||
|
800: white,
|
||||||
|
900: white,
|
||||||
|
A100: $black-87-opacity,
|
||||||
|
A200: $black-87-opacity,
|
||||||
|
A400: $black-87-opacity,
|
||||||
|
A700: white,
|
||||||
|
)
|
||||||
|
);
|
||||||
// Define the palettes for your theme using the Material Design palettes available in palette.scss
|
// Define the palettes for your theme using the Material Design palettes available in palette.scss
|
||||||
// (imported above). For each palette, you can optionally specify a default, lighter, and darker
|
// (imported above). For each palette, you can optionally specify a default, lighter, and darker
|
||||||
// hue. Available color palettes: https://material.io/design/color/
|
// hue. Available color palettes: https://material.io/design/color/
|
||||||
|
|
||||||
$light-primary: mat-palette($caos-light-brand);
|
$light-primary: mat-palette($caos-light-brand);
|
||||||
$light-accent: mat-palette($mat-pink);
|
$light-accent:mat-palette($caos-accent-color);
|
||||||
$light-warn: mat-palette($mat-red);
|
$light-warn: mat-palette($mat-red);
|
||||||
|
|
||||||
$dark-primary: mat-palette($caos-dark-brand);
|
$dark-primary: mat-palette($caos-dark-brand);
|
||||||
$dark-accent: mat-palette($mat-pink, 500, 700, A700);
|
$dark-accent: mat-palette($mat-pink);
|
||||||
$dark-warn: mat-palette($mat-red);
|
$dark-warn: mat-palette($mat-red);
|
||||||
|
|
||||||
$light-theme: mat-light-theme($light-primary, $light-accent, $light-warn);
|
$light-theme: mat-light-theme($light-primary, $light-accent, $light-warn);
|
||||||
@@ -140,8 +173,9 @@ $custom-typography: mat-typography-config(
|
|||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar-thumb {
|
::-webkit-scrollbar-thumb {
|
||||||
background-color: #5282c1;
|
background-color: #737C8850;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,8 +206,9 @@ $custom-typography: mat-typography-config(
|
|||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar-thumb {
|
::-webkit-scrollbar-thumb {
|
||||||
background-color: #5282c1;
|
background-color: #737C8870;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -48,9 +48,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.root-header {
|
.root-header {
|
||||||
@include mat-elevation(3);
|
box-shadow: 0px 2px 1px -1px rgba(0, 0, 0, 0.1), 0px 1px 1px 0px rgba(0, 0, 0, 0.1), 0px 1px 3px 0px rgba(0, 0, 0, 0.1);
|
||||||
background: $primary-dark !important;
|
background-color: $primary-dark !important;
|
||||||
transition: background .5s ease-in-out;
|
transition: background-color .5s ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
.admin-line {
|
.admin-line {
|
||||||
|
Reference in New Issue
Block a user