mirror of
https://github.com/zitadel/zitadel.git
synced 2024-12-12 02:54:20 +00:00
feat(console): user grant filtering, org domain verification improvements, fix membership detail link (#916)
* user grant filter * add filter input * org domain spinner and reload * user grant filter templates * single selection filter for grants, same dl btn * filter margin * lint style, remove duplicate code * project count as observable * deferred reload on delete * fix user grant formfield * lint styles * fix event propagation on pin, change selection * propagate counter change * admin warn, localstorage, i18n, sidenav impv * overlays * adapt toolbar elevationn, card * color vars, i18n, admin section * fix lint * selection clear on filter * ts lint * Update console/src/assets/i18n/de.json Co-authored-by: Fabi <38692350+fgerschwiler@users.noreply.github.com> * Update console/src/assets/i18n/de.json Co-authored-by: Fabi <38692350+fgerschwiler@users.noreply.github.com> * Update console/src/assets/i18n/en.json Co-authored-by: Fabi <38692350+fgerschwiler@users.noreply.github.com> Co-authored-by: Fabi <38692350+fgerschwiler@users.noreply.github.com>
This commit is contained in:
parent
966e3850ed
commit
42effd8702
@ -29,10 +29,12 @@
|
||||
placeholder="{{'ORG.PAGES.FILTERPLACEHOLDER' | translate}}" #input>
|
||||
</mat-form-field>
|
||||
|
||||
<button [ngClass]="{'active': temporg.id === org?.id}" [disabled]="!temporg.id"
|
||||
*ngFor="let temporg of orgs$ | async" mat-menu-item (click)="setActiveOrg(temporg)">
|
||||
{{temporg?.name ? temporg.name : 'NO NAME'}}
|
||||
</button>
|
||||
<div class="org-wrapper">
|
||||
<button [ngClass]="{'active': temporg.id === org?.id}" [disabled]="!temporg.id"
|
||||
*ngFor="let temporg of orgs$ | async" mat-menu-item (click)="setActiveOrg(temporg)">
|
||||
{{temporg?.name ? temporg.name : 'NO NAME'}}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<button class="show-all" mat-menu-item
|
||||
[routerLink]="[ '/org/overview' ]">{{'MENU.SHOWORGS' | translate}}</button>
|
||||
@ -70,8 +72,10 @@
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="iamuser$ | async">
|
||||
<div class="divider">
|
||||
<div @navitem class="divider">
|
||||
<div class="line"></div>
|
||||
<span>{{'MENU.ADMINSECTION' | translate}}</span>
|
||||
<div class="hiddenline"></div>
|
||||
</div>
|
||||
<a @navitem class="nav-item" [routerLinkActive]="['active']" [routerLink]="[ '/iam']">
|
||||
<i class="icon las la-gem"></i>
|
||||
@ -92,7 +96,7 @@
|
||||
<div @navitem class="divider">
|
||||
<div class="line"></div>
|
||||
<span>{{'MENU.PROJECTSSECTION' | translate}}</span>
|
||||
<div class="line"></div>
|
||||
<div class="hiddenline"></div>
|
||||
</div>
|
||||
|
||||
<a @navitem class="nav-item" [routerLinkActive]="['active']"
|
||||
@ -102,17 +106,19 @@
|
||||
<div class="c_label">
|
||||
<span>{{org?.name ? org.name : 'MENU.ORGANIZATION' | translate}}
|
||||
{{'MENU.PROJECT' | translate}} </span>
|
||||
<span *ngIf="ownedProjectsCount as ownedPCount"
|
||||
class="count">{{ownedPCount}}</span>
|
||||
<span *ngIf="(mgmtService?.ownedProjectsCount | async)"
|
||||
class="count">{{mgmtService?.ownedProjectsCount | async}}</span>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<a @navitem *ngIf="grantedProjectsCount as grantPCount" class="nav-item"
|
||||
[routerLinkActive]="['active']" [routerLink]="[ '/granted-projects']">
|
||||
<a @navitem
|
||||
*ngIf="mgmtService?.grantedProjectsCount && (mgmtService?.grantedProjectsCount | async)"
|
||||
class="nav-item" [routerLinkActive]="['active']"
|
||||
[routerLink]="[ '/granted-projects']">
|
||||
<i class="icon las la-layer-group"></i>
|
||||
<div class="c_label">
|
||||
<span>{{ 'MENU.GRANTEDPROJECT' | translate }}</span>
|
||||
<span class="count">{{grantPCount}}</span>
|
||||
<span class="count">{{mgmtService?.grantedProjectsCount | async}}</span>
|
||||
</div>
|
||||
</a>
|
||||
</ng-template>
|
||||
@ -122,7 +128,7 @@
|
||||
<div class="line"></div>
|
||||
<span class="label">
|
||||
{{ 'MENU.USERSECTION' | translate }}</span>
|
||||
<div class="line"></div>
|
||||
<div class="hiddenline"></div>
|
||||
</div>
|
||||
|
||||
<a @navitem class="nav-item" [routerLinkActive]="['active']"
|
||||
@ -144,7 +150,7 @@
|
||||
<div class="line"></div>
|
||||
<span class="label">
|
||||
{{ 'MENU.GRANTSECTION' | translate }}</span>
|
||||
<div class="line"></div>
|
||||
<div class="hiddenline"></div>
|
||||
</div>
|
||||
|
||||
<a @navitem class="nav-item" [routerLinkActive]="['active']" [routerLink]="[ '/grants']"
|
||||
@ -161,13 +167,18 @@
|
||||
</div>
|
||||
</mat-drawer>
|
||||
<mat-drawer-content class="content">
|
||||
<div @toolbar *ngIf="iamuser$ | async" class="admin-line" matTooltip="IAM Administrator">
|
||||
<span>{{'MENU.IAMADMIN' | translate}}</span>
|
||||
</div>
|
||||
<div class="router" [@routeAnimations]="prepareRoute(outlet)">
|
||||
<router-outlet #outlet="outlet"></router-outlet>
|
||||
</div>
|
||||
</mat-drawer-content>
|
||||
</mat-drawer-container>
|
||||
<div @toolbar *ngIf="iamuser$ | async" class="admin-line" [ngClass]="{'expanded': !hideAdminWarn}"
|
||||
matTooltip="IAM Administrator">
|
||||
<button [matTooltip]="!hideAdminWarn ? 'Unpin': 'Pin'" (click)="toggleAdminHide()" mat-icon-button>
|
||||
<mat-icon *ngIf="!hideAdminWarn" svgIcon="mdi_pin"></mat-icon>
|
||||
<mat-icon *ngIf="hideAdminWarn" svgIcon="mdi_pin_outline"></mat-icon>
|
||||
</button>
|
||||
<span>{{'MENU.IAMADMIN' | translate}}</span>
|
||||
</div>
|
||||
</ng-container>
|
||||
</ng-container>
|
@ -66,13 +66,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.admin-line {
|
||||
font-size: 12px;
|
||||
padding: 4px 2rem;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.main-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@ -222,6 +215,13 @@
|
||||
margin: .5rem 0;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.hiddenline {
|
||||
display: block;
|
||||
visibility: hidden;
|
||||
// flex: 1;
|
||||
width: 4rem;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin textvar($theme) {
|
||||
@ -255,3 +255,8 @@
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.org-wrapper {
|
||||
max-height: 350px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
@ -23,7 +23,6 @@ import { AuthenticationService } from './services/authentication.service';
|
||||
import { GrpcAuthService } from './services/grpc-auth.service';
|
||||
import { ManagementService } from './services/mgmt.service';
|
||||
import { ThemeService } from './services/theme.service';
|
||||
import { ToastService } from './services/toast.service';
|
||||
import { UpdateService } from './services/update.service';
|
||||
|
||||
@Component({
|
||||
@ -57,13 +56,11 @@ export class AppComponent implements OnDestroy {
|
||||
|
||||
public showProjectSection: boolean = false;
|
||||
|
||||
public grantedProjectsCount: number = 0;
|
||||
public ownedProjectsCount: number = 0;
|
||||
|
||||
public filterControl: FormControl = new FormControl('');
|
||||
private authSub: Subscription = new Subscription();
|
||||
private orgSub: Subscription = new Subscription();
|
||||
|
||||
public hideAdminWarn: boolean = true;
|
||||
constructor(
|
||||
public viewPortScroller: ViewportScroller,
|
||||
@Inject('windowObject') public window: Window,
|
||||
@ -73,15 +70,14 @@ export class AppComponent implements OnDestroy {
|
||||
private breakpointObserver: BreakpointObserver,
|
||||
public overlayContainer: OverlayContainer,
|
||||
private themeService: ThemeService,
|
||||
private mgmtService: ManagementService,
|
||||
public mgmtService: ManagementService,
|
||||
public matIconRegistry: MatIconRegistry,
|
||||
public domSanitizer: DomSanitizer,
|
||||
private toast: ToastService,
|
||||
private router: Router,
|
||||
update: UpdateService,
|
||||
@Inject(DOCUMENT) private document: Document,
|
||||
) {
|
||||
console.log('%cWait!', 'text-shadow: -1px 0 black, 0 1px black, 1px 0 black, 0 -1px black; color: #5282c1; font-size: 50px');
|
||||
console.log('%cWait!', 'text-shadow: -1px 0 black, 0 1px black, 1px 0 black, 0 -1px black; color: #5469D4; font-size: 50px');
|
||||
console.log('%cInserting something here could give attackers access to your zitadel account.', 'color: red; font-size: 18px');
|
||||
console.log('%cIf you don\'t know exactly what you\'re doing, close the window and stay on the safe side', 'font-size: 16px');
|
||||
console.log('%cIf you know exactly what you are doing, you should work for us', 'font-size: 16px');
|
||||
@ -179,6 +175,8 @@ export class AppComponent implements OnDestroy {
|
||||
value.trim().toLowerCase(),
|
||||
);
|
||||
});
|
||||
|
||||
this.hideAdminWarn = localStorage.getItem('hideAdministratorWarning') === 'true' ? true : false;
|
||||
}
|
||||
|
||||
public ngOnDestroy(): void {
|
||||
@ -186,6 +184,11 @@ export class AppComponent implements OnDestroy {
|
||||
this.orgSub.unsubscribe();
|
||||
}
|
||||
|
||||
public toggleAdminHide(): void {
|
||||
this.hideAdminWarn = !this.hideAdminWarn;
|
||||
localStorage.setItem('hideAdministratorWarning', this.hideAdminWarn.toString());
|
||||
}
|
||||
|
||||
public loadOrgs(filter?: string): void {
|
||||
let query;
|
||||
if (filter) {
|
||||
@ -249,13 +252,9 @@ export class AppComponent implements OnDestroy {
|
||||
private getProjectCount(): void {
|
||||
this.authService.isAllowed(['project.read$']).subscribe((allowed) => {
|
||||
if (allowed) {
|
||||
this.mgmtService.SearchProjects(0, 0).then(res => {
|
||||
this.ownedProjectsCount = res.toObject().totalResult;
|
||||
});
|
||||
this.mgmtService.SearchProjects(0, 0);
|
||||
|
||||
this.mgmtService.SearchGrantedProjects(0, 0).then(res => {
|
||||
this.grantedProjectsCount = res.toObject().totalResult;
|
||||
});
|
||||
this.mgmtService.SearchGrantedProjects(0, 0);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -42,5 +42,6 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,7 @@
|
||||
box-sizing: border-box;
|
||||
border-radius: .5rem;
|
||||
outline: none;
|
||||
height: 100%;
|
||||
|
||||
.selection-icon {
|
||||
opacity: 0;
|
||||
|
@ -5,6 +5,7 @@
|
||||
margin-bottom: 1rem;
|
||||
font-weight: 400;
|
||||
margin-top: 1rem;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
@mixin changes-theme($theme) {
|
||||
|
@ -8,6 +8,7 @@
|
||||
display: relative;
|
||||
width: 100%;
|
||||
overflow-y: auto;
|
||||
padding-bottom: 50px;
|
||||
|
||||
&.hidden {
|
||||
flex-basis: 100%;
|
||||
|
@ -7,17 +7,10 @@
|
||||
$primary-dark: mat-color($primary, A800);
|
||||
/* stylelint-enable */
|
||||
|
||||
$lighter-color: rgba(mat-color($primary, 300), .5);
|
||||
|
||||
.meta-wrapper {
|
||||
.meta {
|
||||
position: relative;
|
||||
flex: 1 0 300px;
|
||||
background: linear-gradient(to bottom right, rgba($lighter-color, .05) 20%, transparent 50%);
|
||||
|
||||
&.hidden {
|
||||
background: linear-gradient(to bottom right, rgba($lighter-color, .05), transparent 50%);
|
||||
}
|
||||
|
||||
&::after {
|
||||
border-left: 2px solid $primary-color;
|
||||
@ -27,7 +20,7 @@
|
||||
left top,
|
||||
left bottom,
|
||||
from($primary-color),
|
||||
to($primary-dark),
|
||||
to(rgb(0, 0, 0, .5)),
|
||||
color-stop(
|
||||
01,
|
||||
$primary-dark
|
||||
@ -39,7 +32,7 @@
|
||||
left top,
|
||||
left bottom,
|
||||
from($primary-color),
|
||||
to($primary-dark),
|
||||
to(rgb(0, 0, 0, .5)),
|
||||
color-stop(
|
||||
01,
|
||||
$primary-dark
|
||||
|
@ -1,5 +1,5 @@
|
||||
.default {
|
||||
color: #5282c1;
|
||||
color: var(--color-main);
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
.default {
|
||||
color: #5282c1;
|
||||
color: var(--color-main);
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
.default {
|
||||
color: #5282c1;
|
||||
color: var(--color-main);
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
.default {
|
||||
color: #5282c1;
|
||||
color: var(--color-main);
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
|
@ -16,8 +16,9 @@ h1 {
|
||||
margin: .5rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 200px;
|
||||
min-height: 250px;
|
||||
padding: 1rem;
|
||||
height: 100%;
|
||||
|
||||
@media only screen and (max-width: 450px) {
|
||||
flex-basis: 100%;
|
||||
|
@ -103,7 +103,7 @@ export class UserGrantsDataSource extends DataSource<UserGrant.AsObject> {
|
||||
break;
|
||||
default:
|
||||
this.loadingSubject.next(true);
|
||||
const promise3 = this.userService.SearchUserGrants(pageSize, pageSize * pageIndex, []);
|
||||
const promise3 = this.userService.SearchUserGrants(pageSize, pageSize * pageIndex, queries ?? []);
|
||||
this.loadResponse(promise3);
|
||||
break;
|
||||
}
|
||||
|
@ -1,6 +1,12 @@
|
||||
<app-refresh-table [loading]="dataSource?.loading$ | async" (refreshed)="changePage()"
|
||||
[emitRefreshOnPreviousRoutes]="refreshOnPreviousRoutes" [timestamp]="dataSource?.viewTimestamp"
|
||||
[dataSize]="dataSource?.totalResult" [selection]="selection">
|
||||
<mat-form-field @appearfade *ngIf="userGrantSearchKey != undefined" actions class="filtername">
|
||||
<mat-label>{{'USER.PAGES.FILTER' | translate}}</mat-label>
|
||||
<input matInput (keyup)="applyFilter($event)"
|
||||
[placeholder]="('USER.TABLE.FILTER.' + userGrantSearchKey.toString()) | translate" #input>
|
||||
</mat-form-field>
|
||||
|
||||
<button color="warn" matTooltip="{{'GRANTS.DELETE' | translate}}" class="icon-button" mat-icon-button actions
|
||||
(click)="deleteGrantSelection()" *ngIf="selection.hasValue() && disableDelete == false">
|
||||
<i class="las la-trash"></i>
|
||||
@ -33,19 +39,28 @@
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="user">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.GRANT.USER' | translate }} </th>
|
||||
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.GRANT.USER' | translate }}
|
||||
<template [ngTemplateOutlet]="templateRef"
|
||||
[ngTemplateOutletContext]="{key: UserGrantSearchKey.USERGRANTSEARCHKEY_DISPLAY_NAME}"></template>
|
||||
</th>
|
||||
<td mat-cell *matCellDef="let grant">
|
||||
{{grant?.displayName}}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="org">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.GRANT.GRANTEDORGDOMAIN' | translate }} </th>
|
||||
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.GRANT.GRANTEDORGDOMAIN' | translate }}
|
||||
<template [ngTemplateOutlet]="templateRef"
|
||||
[ngTemplateOutletContext]="{key: UserGrantSearchKey.USERGRANTSEARCHKEY_ORG_NAME}"></template>
|
||||
</th>
|
||||
<td mat-cell *matCellDef="let grant">
|
||||
{{grant.orgName}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="projectId">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.GRANT.PROJECTNAME' | translate }} </th>
|
||||
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.GRANT.PROJECTNAME' | translate }}
|
||||
<template [ngTemplateOutlet]="templateRef"
|
||||
[ngTemplateOutletContext]="{key: UserGrantSearchKey.USERGRANTSEARCHKEY_PROJECT_NAME}"></template>
|
||||
</th>
|
||||
<td mat-cell *matCellDef="let grant">
|
||||
{{grant.projectName}} </td>
|
||||
</ng-container>
|
||||
@ -63,29 +78,32 @@
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="roleNamesList">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.GRANT.ROLENAMESLIST' | translate }} </th>
|
||||
<th mat-header-cell *matHeaderCellDef>
|
||||
{{ 'PROJECT.GRANT.ROLENAMESLIST' | translate }}
|
||||
<template [ngTemplateOutlet]="templateRef"
|
||||
[ngTemplateOutletContext]="{key: UserGrantSearchKey.USERGRANTSEARCHKEY_ROLE_KEY}"></template>
|
||||
</th>
|
||||
<td mat-cell *matCellDef="let grant; let i = index">
|
||||
<ng-container *ngIf="context === UserGrantContext.USER || context === UserGrantContext.NONE">
|
||||
<ng-container
|
||||
*ngIf="(grant.grantId && loadedGrantId !== grant.grantId) || (loadedProjectId !== grant.projectId)">
|
||||
<div class="flex-row">
|
||||
<span class="role" *ngFor="let role of grant.roleKeysList">{{ role }}</span>
|
||||
<button mat-stroked-button
|
||||
*ngIf="grant.grantId ? loadedGrantId !== grant.grantId : loadedProjectId !== grant.projectId"
|
||||
[disabled]="disableWrite || !((['user.grant.write$'] | hasRole | async) || ((context === UserGrantContext.OWNED_PROJECT ? ['user.grant.write:' + grant?.projectId] : context === UserGrantContext.GRANTED_PROJECT ? ['user.grant.write:' + grant?.grantId] : []) | hasRole | async))"
|
||||
(click)="grant.grantId ? getGrantRoleOptions(grant.grantId, grant.projectId) : getProjectRoleOptions(grant.projectId)"
|
||||
matTooltip="{{'ACTIONS.CHANGE' | translate}}">
|
||||
<i class="las la-edit"></i>
|
||||
{{'ACTIONS.EDIT' | translate}}
|
||||
</button>
|
||||
<ng-container
|
||||
*ngIf="(context === UserGrantContext.USER || context === UserGrantContext.NONE) && (grant.grantId && grantToEdit !== grant.id) || (grantToEdit !== grant.id)">
|
||||
<div class="flex-row">
|
||||
<div class="role">
|
||||
<span *ngFor="let role of grant.roleKeysList">{{ role }}</span>
|
||||
</div>
|
||||
</ng-container>
|
||||
<span class="fill-space"></span>
|
||||
<button mat-stroked-button
|
||||
*ngIf="grant.grantId ? grantToEdit !== grant.id : grantToEdit !== grant.id"
|
||||
[disabled]="disableWrite || !((['user.grant.write$'] | hasRole | async) || ((context === UserGrantContext.OWNED_PROJECT ? ['user.grant.write:' + grant?.projectId] : context === UserGrantContext.GRANTED_PROJECT ? ['user.grant.write:' + grant?.grantId] : []) | hasRole | async))"
|
||||
(click)="loadGrantOptions(grant)" matTooltip="{{'ACTIONS.CHANGE' | translate}}">
|
||||
<i class="las la-edit"></i>
|
||||
{{'ACTIONS.EDIT' | translate}}
|
||||
</button>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ng-container
|
||||
*ngIf="context === UserGrantContext.OWNED_PROJECT || context === UserGrantContext.USER || context === UserGrantContext.NONE">
|
||||
<mat-form-field class="form-field" appearance="outline"
|
||||
*ngIf="loadedProjectId && loadedProjectId === grant.projectId">
|
||||
*ngIf="(context === UserGrantContext.OWNED_PROJECT || context === UserGrantContext.USER || context === UserGrantContext.NONE) && grantToEdit == grant.id && loadedProjectId && loadedProjectId === grant.projectId">
|
||||
<mat-form-field class="form-field" appearance="outline">
|
||||
<mat-label>{{ 'PROJECT.GRANT.ROLENAMESLIST' | translate }}</mat-label>
|
||||
<mat-select [(ngModel)]="grant.roleKeysList" multiple
|
||||
[disabled]="disableWrite || !((['user.grant.write$'] | hasRole | async) || ((context === UserGrantContext.OWNED_PROJECT ? ['user.grant.write:' + grant?.projectId] : context === UserGrantContext.GRANTED_PROJECT ? ['user.grant.write:' + grant?.grantId] : []) | hasRole | async))"
|
||||
@ -95,12 +113,15 @@
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<button *ngIf="context === UserGrantContext.USER || context === UserGrantContext.NONE"
|
||||
mat-icon-button (click)="grantToEdit=''">
|
||||
<mat-icon>close</mat-icon>
|
||||
</button>
|
||||
</ng-container>
|
||||
|
||||
<ng-container
|
||||
*ngIf="context === UserGrantContext.GRANTED_PROJECT || context === UserGrantContext.USER || context === UserGrantContext.NONE">
|
||||
<mat-form-field class="form-field" appearance="outline"
|
||||
*ngIf="loadedGrantId && loadedGrantId === grant.grantId">
|
||||
*ngIf="(context === UserGrantContext.GRANTED_PROJECT || context === UserGrantContext.USER || context === UserGrantContext.NONE) && loadedGrantId && loadedGrantId === grant.grantId && grantToEdit == grant.id">
|
||||
<mat-form-field class="form-field" appearance="outline">
|
||||
<mat-label>{{ 'PROJECT.GRANT.ROLENAMESLIST' | translate }}</mat-label>
|
||||
<mat-select [(ngModel)]="grant.roleKeysList" multiple
|
||||
[disabled]="disableWrite || !((['user.grant.write$'] | hasRole | async) || ((context === UserGrantContext.OWNED_PROJECT ? ['user.grant.write:' + grant?.projectId] : context === UserGrantContext.GRANTED_PROJECT ? ['user.grant.write:' + grant?.grantId] : []) | hasRole | async))"
|
||||
@ -110,6 +131,10 @@
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<button *ngIf="context === UserGrantContext.USER || context === UserGrantContext.NONE"
|
||||
mat-icon-button (click)="grantToEdit=''">
|
||||
<mat-icon>close</mat-icon>
|
||||
</button>
|
||||
</ng-container>
|
||||
</td>
|
||||
</ng-container>
|
||||
@ -124,3 +149,10 @@
|
||||
</mat-paginator>
|
||||
</div>
|
||||
</app-refresh-table>
|
||||
|
||||
<ng-template #templateRef let-key="key">
|
||||
<button class="search-button" mat-icon-button (click)="setFilter(key)">
|
||||
<mat-icon *ngIf="this.userGrantSearchKey != key">search</mat-icon>
|
||||
<mat-icon *ngIf="this.userGrantSearchKey == key">search_off</mat-icon>
|
||||
</button>
|
||||
</ng-template>
|
@ -21,6 +21,19 @@
|
||||
}
|
||||
}
|
||||
|
||||
th {
|
||||
.search-button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&.search-active {
|
||||
.search-button {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.selection {
|
||||
width: 50px;
|
||||
max-width: 50px;
|
||||
@ -35,15 +48,22 @@
|
||||
|
||||
.flex-row {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-direction: row;
|
||||
max-width: 400px;
|
||||
|
||||
.role {
|
||||
display: block;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: .25rem;
|
||||
font-size: 14px;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
justify-items: center;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.fill-space {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
button {
|
||||
@ -60,3 +80,7 @@
|
||||
.fill-space {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.filtername {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
@ -1,10 +1,19 @@
|
||||
import { SelectionModel } from '@angular/cdk/collections';
|
||||
import { AfterViewInit, Component, Input, OnInit, ViewChild } from '@angular/core';
|
||||
import { MatInput } from '@angular/material/input';
|
||||
import { MatPaginator, PageEvent } from '@angular/material/paginator';
|
||||
import { MatSelectChange } from '@angular/material/select';
|
||||
import { MatTable } from '@angular/material/table';
|
||||
import { tap } from 'rxjs/operators';
|
||||
import { ProjectRoleView, UserGrant, UserGrantView } from 'src/app/proto/generated/management_pb';
|
||||
import { enterAnimations } from 'src/app/animations';
|
||||
import {
|
||||
ProjectRoleView,
|
||||
SearchMethod,
|
||||
UserGrant,
|
||||
UserGrantSearchKey,
|
||||
UserGrantSearchQuery,
|
||||
UserGrantView,
|
||||
} from 'src/app/proto/generated/management_pb';
|
||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
@ -14,12 +23,17 @@ import { UserGrantContext, UserGrantsDataSource } from './user-grants-datasource
|
||||
selector: 'app-user-grants',
|
||||
templateUrl: './user-grants.component.html',
|
||||
styleUrls: ['./user-grants.component.scss'],
|
||||
animations: [
|
||||
enterAnimations,
|
||||
],
|
||||
})
|
||||
export class UserGrantsComponent implements OnInit, AfterViewInit {
|
||||
public userGrantSearchKey: UserGrantSearchKey | undefined = undefined;
|
||||
public UserGrantSearchKey: any = UserGrantSearchKey;
|
||||
|
||||
public INITIAL_PAGE_SIZE: number = 50;
|
||||
@Input() context: UserGrantContext = UserGrantContext.NONE;
|
||||
@Input() refreshOnPreviousRoutes: string[] = [];
|
||||
public grants: UserGrantView.AsObject[] = [];
|
||||
|
||||
public dataSource!: UserGrantsDataSource;
|
||||
public selection: SelectionModel<UserGrantView.AsObject> = new SelectionModel<UserGrantView.AsObject>(true, []);
|
||||
@ -32,6 +46,7 @@ export class UserGrantsComponent implements OnInit, AfterViewInit {
|
||||
@Input() userId: string = '';
|
||||
@Input() projectId: string = '';
|
||||
@Input() grantId: string = '';
|
||||
@ViewChild('input') public filter!: MatInput;
|
||||
|
||||
public grantRoleOptions: string[] = [];
|
||||
public projectRoleOptions: ProjectRoleView.AsObject[] = [];
|
||||
@ -39,6 +54,7 @@ export class UserGrantsComponent implements OnInit, AfterViewInit {
|
||||
|
||||
public loadedGrantId: string = '';
|
||||
public loadedProjectId: string = '';
|
||||
public grantToEdit: string = '';
|
||||
|
||||
public UserGrantContext: any = UserGrantContext;
|
||||
|
||||
@ -89,7 +105,16 @@ export class UserGrantsComponent implements OnInit, AfterViewInit {
|
||||
.subscribe();
|
||||
}
|
||||
|
||||
private loadGrantsPage(): void {
|
||||
private loadGrantsPage(filterValue?: string): void {
|
||||
let queries: UserGrantSearchQuery[] = [];
|
||||
if (this.userGrantSearchKey !== undefined && filterValue) {
|
||||
const query = new UserGrantSearchQuery();
|
||||
query.setKey(this.userGrantSearchKey);
|
||||
query.setMethod(SearchMethod.SEARCHMETHOD_CONTAINS_IGNORE_CASE);
|
||||
query.setValue(filterValue);
|
||||
queries = [query];
|
||||
}
|
||||
|
||||
this.dataSource.loadGrants(
|
||||
this.context,
|
||||
this.paginator?.pageIndex ?? 0,
|
||||
@ -99,6 +124,7 @@ export class UserGrantsComponent implements OnInit, AfterViewInit {
|
||||
grantId: this.grantId,
|
||||
userId: this.userId,
|
||||
},
|
||||
queries,
|
||||
);
|
||||
}
|
||||
|
||||
@ -114,7 +140,16 @@ export class UserGrantsComponent implements OnInit, AfterViewInit {
|
||||
this.dataSource.grantsSubject.value.forEach(row => this.selection.select(row));
|
||||
}
|
||||
|
||||
public getGrantRoleOptions(grantId: string, projectId: string): void {
|
||||
public loadGrantOptions(grant: UserGrantView.AsObject): void {
|
||||
this.grantToEdit = grant.id;
|
||||
if (grant.grantId && grant.projectId) {
|
||||
this.getGrantRoleOptions(grant.grantId, grant.projectId);
|
||||
} else if (grant.projectId) {
|
||||
this.getProjectRoleOptions(grant.projectId);
|
||||
}
|
||||
}
|
||||
|
||||
private getGrantRoleOptions(grantId: string, projectId: string): void {
|
||||
this.mgmtService.GetGrantedProjectByID(projectId, grantId).then(resp => {
|
||||
this.loadedGrantId = grantId;
|
||||
this.grantRoleOptions = resp.toObject().roleKeysList;
|
||||
@ -123,7 +158,7 @@ export class UserGrantsComponent implements OnInit, AfterViewInit {
|
||||
});
|
||||
}
|
||||
|
||||
public getProjectRoleOptions(projectId: string): void {
|
||||
private getProjectRoleOptions(projectId: string): void {
|
||||
this.mgmtService.SearchProjectRoles(projectId, 100, 0).then(resp => {
|
||||
this.loadedProjectId = projectId;
|
||||
this.projectRoleOptions = resp.toObject().resultList;
|
||||
@ -168,4 +203,26 @@ export class UserGrantsComponent implements OnInit, AfterViewInit {
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
public applyFilter(event: Event): void {
|
||||
this.selection.clear();
|
||||
const filterValue = (event.target as HTMLInputElement).value;
|
||||
|
||||
this.loadGrantsPage(filterValue);
|
||||
}
|
||||
|
||||
public setFilter(key: UserGrantSearchKey): void {
|
||||
setTimeout(() => {
|
||||
if (this.filter) {
|
||||
(this.filter as any).nativeElement.focus();
|
||||
}
|
||||
}, 100);
|
||||
|
||||
if (this.userGrantSearchKey !== key) {
|
||||
this.userGrantSearchKey = key;
|
||||
} else {
|
||||
this.userGrantSearchKey = undefined;
|
||||
this.loadGrantsPage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatPaginatorModule } from '@angular/material/paginator';
|
||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
@ -39,6 +40,7 @@ import { UserGrantsComponent } from './user-grants.component';
|
||||
MatCheckboxModule,
|
||||
MatTooltipModule,
|
||||
MatSelectModule,
|
||||
MatInputModule,
|
||||
MatFormFieldModule,
|
||||
TranslateModule,
|
||||
HasRolePipeModule,
|
||||
|
@ -24,6 +24,23 @@
|
||||
{{'HOME.IAM'| translate}}</h2>
|
||||
<p>{{'HOME.IAM_DESC'| translate}}</p>
|
||||
</div>
|
||||
|
||||
<ng-template appHasRole [appHasRole]="['org.create','iam.write']">
|
||||
<a class="short-link" [routerLink]="[ '/org', 'create']">{{'HOME.IAM_CREATE_ORG' | translate}}<i
|
||||
class="las la-link"></i></a>
|
||||
</ng-template>
|
||||
<ng-template appHasRole [appHasRole]="['iam.policy.read']">
|
||||
<a class="short-link"
|
||||
[routerLink]="[ '/iam', 'policy','iam']">{{'HOME.IAM_POLICY_IAM' | translate}}<i
|
||||
class="las la-link"></i></a>
|
||||
<a class="short-link"
|
||||
[routerLink]="[ '/iam', 'policy','complexity']">{{'HOME.IAM_POLICY_COMPLEXITY' | translate}}<i
|
||||
class="las la-link"></i></a>
|
||||
<a class="short-link"
|
||||
[routerLink]="[ '/iam', 'policy','login']">{{'HOME.IAM_POLICY_LOGIN' | translate}}<i
|
||||
class="las la-link"></i></a>
|
||||
</ng-template>
|
||||
|
||||
<span class="fill-space"></span>
|
||||
<div class="footer">
|
||||
<a color="primary" mat-stroked-button [routerLink]="['/iam']">{{'HOME.IAM_BUTTON' | translate}}</a>
|
||||
@ -37,6 +54,9 @@
|
||||
{{'HOME.SECURITYANDPRIVACY'| translate}}</h2>
|
||||
<p>{{'HOME.SECURITYANDPRIVACY_DESC'| translate}}</p>
|
||||
</div>
|
||||
<a class="short-link" [routerLink]="[ '/users', 'me','password']">{{'HOME.CHANGE_PWD' | translate}}<i
|
||||
class="las la-link"></i></a>
|
||||
|
||||
<span class="fill-space"></span>
|
||||
<div class="footer">
|
||||
<a color="primary" mat-stroked-button
|
||||
@ -51,6 +71,11 @@
|
||||
<i class="icon las la-layer-group"></i>
|
||||
{{'HOME.PROJECTS'| translate}}</h2>
|
||||
<p>{{'HOME.PROJECTS_DESC'| translate}}</p>
|
||||
<ng-template appHasRole [appHasRole]="['project.create']">
|
||||
<a class="short-link"
|
||||
[routerLink]="[ '/projects', 'create']">{{'HOME.PROJECTS_NEW_LINK' | translate}}<i
|
||||
class="las la-link"></i></a>
|
||||
</ng-template>
|
||||
</div>
|
||||
<span class="fill-space"></span>
|
||||
<div class="footer">
|
||||
@ -66,6 +91,19 @@
|
||||
<h2> <i class="icon las la-archway"></i>
|
||||
{{'HOME.PROTECTION'| translate}}</h2>
|
||||
<p>{{'HOME.PROTECTION_DESC'| translate}}</p>
|
||||
<ng-template appHasRole [appHasRole]="['iam.policy.read']">
|
||||
<a class="short-link"
|
||||
[routerLink]="[ '/org', 'policy','iam']">{{'HOME.ORG_POLICY_IAM' | translate}}<i
|
||||
class="las la-link"></i></a>
|
||||
</ng-template>
|
||||
<ng-template appHasRole [appHasRole]="['policy.read']">
|
||||
<a class="short-link"
|
||||
[routerLink]="[ '/org', 'policy','complexity']">{{'HOME.ORG_POLICY_COMPLEXITY' | translate}}<i
|
||||
class="las la-link"></i></a>
|
||||
<a class="short-link"
|
||||
[routerLink]="[ '/org', 'policy','login']">{{'HOME.ORG_POLICY_LOGIN' | translate}}<i
|
||||
class="las la-link"></i></a>
|
||||
</ng-template>
|
||||
</div>
|
||||
<span class="fill-space"></span>
|
||||
<div class="footer">
|
||||
@ -82,6 +120,18 @@
|
||||
<i class="las la-users"></i>
|
||||
{{'HOME.USERS'| translate}}</h2>
|
||||
<p>{{'HOME.USERS_DESC'| translate}}</p>
|
||||
<ng-template appHasRole [appHasRole]="['user.read(:[0-9]*)?']">
|
||||
<a class="short-link"
|
||||
[routerLink]="[ '/users', 'list', 'humans']">{{'HOME.USERS_HUMANS' | translate}}<i
|
||||
class="las la-link"></i></a>
|
||||
<a class="short-link"
|
||||
[routerLink]="[ '/users', 'list', 'machines']">{{'HOME.USERS_MACHINES' | translate}}<i
|
||||
class="las la-link"></i></a>
|
||||
</ng-template>
|
||||
<ng-template appHasRole [appHasRole]="['user.read']">
|
||||
<a class="short-link" [routerLink]="[ '/users', 'create']">{{'HOME.USERS_CREATE' | translate}}<i
|
||||
class="las la-link"></i></a>
|
||||
</ng-template>
|
||||
</div>
|
||||
<span class="fill-space"></span>
|
||||
<div class="footer">
|
||||
|
@ -32,7 +32,7 @@
|
||||
justify-content: space-evenly;
|
||||
|
||||
.item {
|
||||
flex: 1 1 45%;
|
||||
flex: 1 0 45%;
|
||||
margin: 0 1rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@ -80,3 +80,25 @@
|
||||
margin-top: 3rem;
|
||||
}
|
||||
}
|
||||
|
||||
.short-link {
|
||||
margin-bottom: .5rem;
|
||||
font-size: 15px;
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
position: relative;
|
||||
|
||||
.las {
|
||||
font-size: 1.5rem !important;
|
||||
height: 1.5rem;
|
||||
visibility: hidden;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.las {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,8 +12,10 @@
|
||||
{{'ORG.PAGES.ORGDOMAIN.TYPES.'+ domain.validationType | translate}}</p>
|
||||
<div *ngIf="domain.validationType !== OrgDomainValidationType.ORGDOMAINVALIDATIONTYPE_UNSPECIFIED"
|
||||
class="btn-container">
|
||||
<button color="primary" type="submit" mat-raised-button *ngIf="!(dns || http)"
|
||||
(click)="validate()">{{ 'ACTIONS.VERIFY' | translate }}</button>
|
||||
<button color="primary" type="submit" mat-raised-button *ngIf="!(dns || http)" (click)="validate()">
|
||||
{{ 'ACTIONS.VERIFY' | translate }}
|
||||
</button>
|
||||
<mat-spinner class="spinner" *ngIf="validating" diameter="20" mode="indeterminate"></mat-spinner>
|
||||
|
||||
<button *ngIf="!showNew" mat-stroked-button color="primary"
|
||||
(click)="showNew = true">{{'ORG.PAGES.ORGDOMAIN.REQUESTNEWTOKEN' | translate}}</button>
|
||||
@ -34,8 +36,11 @@
|
||||
<div class="btn-container">
|
||||
<button mat-stroked-button (click)="saveFile()"
|
||||
color="primary">{{ 'ORG.PAGES.DOWNLOAD_FILE' | translate }}</button>
|
||||
<button color="primary" type="submit" mat-raised-button
|
||||
(click)="validate()">{{ 'ACTIONS.VERIFY' | translate }}</button>
|
||||
<button color="primary" class="verify-button" type="submit" mat-raised-button (click)="validate()">
|
||||
<span>{{ 'ACTIONS.VERIFY' | translate }}</span>
|
||||
<mat-spinner class="spinner" *ngIf="!validating" diameter="20" mode="indeterminate"></mat-spinner>
|
||||
</button>
|
||||
<mat-spinner class="spinner" *ngIf="validating" diameter="20" mode="indeterminate"></mat-spinner>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -48,8 +53,10 @@
|
||||
<i *ngIf="copied != dns.token" class="las la-clipboard"></i>
|
||||
<i *ngIf="copied == dns.token" class="las la-clipboard-check"></i>
|
||||
</button>
|
||||
<button color="primary" type="submit" mat-raised-button
|
||||
(click)="validate()">{{ 'ACTIONS.VERIFY' | translate }}</button>
|
||||
<button color="primary" type="submit" mat-raised-button class="verify-button" (click)="validate()">
|
||||
{{ 'ACTIONS.VERIFY' | translate }}
|
||||
</button>
|
||||
<mat-spinner class="spinner" *ngIf="validating" diameter="20" mode="indeterminate"></mat-spinner>
|
||||
</div>
|
||||
<p class="entry">{{dns?.url}}</p>
|
||||
</div>
|
||||
|
@ -1,10 +1,10 @@
|
||||
.btn-container {
|
||||
display: flex;
|
||||
margin: -.5rem;
|
||||
align-items: center;
|
||||
|
||||
button {
|
||||
margin: 1rem .5rem;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -21,6 +21,7 @@ export class DomainVerificationComponent {
|
||||
|
||||
public showNew: boolean = false;
|
||||
|
||||
public validating: boolean = false;
|
||||
constructor(
|
||||
private toast: ToastService,
|
||||
public dialogRef: MatDialogRef<DomainVerificationComponent>,
|
||||
@ -54,10 +55,14 @@ export class DomainVerificationComponent {
|
||||
}
|
||||
|
||||
public validate(): void {
|
||||
this.validating = true;
|
||||
this.mgmtService.ValidateMyOrgDomain(this.domain.domain).then(() => {
|
||||
this.dialogRef.close(false);
|
||||
this.dialogRef.close(true);
|
||||
this.toast.showInfo('ORG.PAGES.ORGDOMAIN.VERIFICATION_SUCCESSFUL', true);
|
||||
this.validating = false;
|
||||
}).catch((error) => {
|
||||
this.toast.showError(error);
|
||||
this.validating = false;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -36,7 +36,7 @@
|
||||
|
||||
.verified,
|
||||
.primary {
|
||||
color: #5282c1;
|
||||
color: var(--color-main);
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
|
@ -79,10 +79,12 @@ export class OrgDetailComponent implements OnInit, OnDestroy {
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
|
||||
this.loadMembers();
|
||||
this.loadDomains();
|
||||
}
|
||||
|
||||
this.mgmtService.SearchMyOrgDomains(0, 100).then(result => {
|
||||
public loadDomains(): void {
|
||||
this.mgmtService.SearchMyOrgDomains().then(result => {
|
||||
this.domains = result.toObject().resultList;
|
||||
this.primaryDomain = this.domains.find(domain => domain.primary)?.domain ?? '';
|
||||
});
|
||||
@ -91,7 +93,7 @@ export class OrgDetailComponent implements OnInit, OnDestroy {
|
||||
public setPrimary(domain: OrgDomainView.AsObject): void {
|
||||
this.mgmtService.setMyPrimaryOrgDomain(domain.domain).then(() => {
|
||||
this.toast.showInfo('ORG.TOAST.SETPRIMARY', true);
|
||||
this.getData();
|
||||
this.loadDomains();
|
||||
}).catch((error) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
@ -202,12 +204,18 @@ export class OrgDetailComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
public verifyDomain(domain: OrgDomainView.AsObject): void {
|
||||
this.dialog.open(DomainVerificationComponent, {
|
||||
const dialogRef = this.dialog.open(DomainVerificationComponent, {
|
||||
data: {
|
||||
domain: domain,
|
||||
},
|
||||
width: '500px',
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe((reload) => {
|
||||
if (reload) {
|
||||
this.loadDomains();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public loadMembers(): void {
|
||||
|
@ -8,6 +8,7 @@ import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatMenuModule } from '@angular/material/menu';
|
||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
import { MatTabsModule } from '@angular/material/tabs';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
@ -51,6 +52,7 @@ import { OrgsRoutingModule } from './orgs-routing.module';
|
||||
MemberCreateDialogModule,
|
||||
MatMenuModule,
|
||||
ChangesModule,
|
||||
MatProgressSpinnerModule,
|
||||
AddDomainDialogModule,
|
||||
TranslateModule,
|
||||
SharedModule,
|
||||
|
@ -22,11 +22,10 @@
|
||||
{{ item.creationDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm' }}</span>
|
||||
<span class="fill-space"></span>
|
||||
</div>
|
||||
<button [ngClass]="{ selected: selection.isSelected(item)}" (click)="selection.toggle(item)" class="edit-button"
|
||||
mat-icon-button>
|
||||
<mat-icon *ngIf="selection.isSelected(item)" svgIcon="mdi_pin"></mat-icon>
|
||||
<mat-icon svgIcon="mdi_pin_outline" *ngIf="!selection.isSelected(item)"></mat-icon>
|
||||
</button>
|
||||
|
||||
<template [ngTemplateOutlet]="toggleButton" [ngTemplateOutletContext]="{key: item}"></template>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@ -47,8 +46,8 @@
|
||||
{{ item.creationDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm' }}</span>
|
||||
<span class="fill-space"></span>
|
||||
</div>
|
||||
<button [ngClass]="{ selected: selection.isSelected(item)}" (click)="selection.toggle(item)" class="edit-button"
|
||||
mat-icon-button>
|
||||
<button [ngClass]="{ selected: selection.isSelected(item)}"
|
||||
(click)="selection.toggle(item); $event.stopPropagation()" class="edit-button" mat-icon-button>
|
||||
<mat-icon *ngIf="selection.isSelected(item)" svgIcon="mdi_pin"></mat-icon>
|
||||
<mat-icon svgIcon="mdi_pin_outline" *ngIf="!selection.isSelected(item)"></mat-icon>
|
||||
</button>
|
||||
@ -56,3 +55,11 @@
|
||||
<p class="n-items" *ngIf="!loading && items.length === 0 && selection.selected.length === 0">
|
||||
{{'PROJECT.PAGES.NOITEMS' | translate}}</p>
|
||||
</div>
|
||||
|
||||
<ng-template #toggleButton let-key="key">
|
||||
<button matTooltip="{{'ACTIONS.PIN' | translate}}" [ngClass]="{ selected: selection.isSelected(key)}"
|
||||
(click)="selection.toggle(key); $event.stopPropagation()" class="edit-button" mat-icon-button>
|
||||
<mat-icon *ngIf="selection.isSelected(key)" svgIcon="mdi_pin"></mat-icon>
|
||||
<mat-icon svgIcon="mdi_pin_outline" *ngIf="!selection.isSelected(key)"></mat-icon>
|
||||
</button>
|
||||
</ng-template>
|
@ -47,11 +47,10 @@ export class GrantedProjectGridComponent implements OnChanges {
|
||||
this.setPrefixedItem('pinned-granted-projects', JSON.stringify(
|
||||
this.selection.selected.map(item => item.projectId),
|
||||
)).then(() => {
|
||||
const filtered = this.notPinned.filter(item => item === selection.added.find(i => i === item));
|
||||
filtered.forEach((f, i) => {
|
||||
this.notPinned.splice(i, 1);
|
||||
selection.added.forEach(item => {
|
||||
const index = this.notPinned.findIndex(i => i.projectId === item.projectId);
|
||||
this.notPinned.splice(index, 1);
|
||||
});
|
||||
|
||||
this.notPinned.push(...selection.removed);
|
||||
});
|
||||
});
|
||||
@ -74,18 +73,10 @@ export class GrantedProjectGridComponent implements OnChanges {
|
||||
const array: string[] = JSON.parse(storageEntry);
|
||||
const toSelect: ProjectGrantView.AsObject[] = this.items.filter((item, index) => {
|
||||
if (array.includes(item.projectId)) {
|
||||
// this.notPinned.splice(index, 1);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
this.selection.select(...toSelect);
|
||||
|
||||
const toNotPinned: ProjectGrantView.AsObject[] = this.items.filter((item, index) => {
|
||||
if (!array.includes(item.projectId)) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
this.notPinned = toNotPinned;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -182,7 +182,10 @@ export class OwnedProjectDetailComponent implements OnInit, OnDestroy {
|
||||
if (resp) {
|
||||
this.mgmtService.RemoveProject(this.projectId).then(() => {
|
||||
this.toast.showInfo('PROJECT.TOAST.DELETED', true);
|
||||
this.router.navigate(['/projects']);
|
||||
const params: Params = {
|
||||
'deferredReload': true,
|
||||
};
|
||||
this.router.navigate(['/projects'], { queryParams: params });
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
|
@ -25,11 +25,9 @@
|
||||
}}</span>
|
||||
<span class="fill-space"></span>
|
||||
</div>
|
||||
<button [ngClass]="{ selected: selection.isSelected(item)}" (click)="selection.toggle(item)" class="edit-button"
|
||||
mat-icon-button>
|
||||
<mat-icon *ngIf="selection.isSelected(item)" svgIcon="mdi_pin"></mat-icon>
|
||||
<mat-icon svgIcon="mdi_pin_outline" *ngIf="!selection.isSelected(item)"></mat-icon>
|
||||
</button>
|
||||
|
||||
<template [ngTemplateOutlet]="deleteButton" [ngTemplateOutletContext]="{key: item}"></template>
|
||||
<template [ngTemplateOutlet]="toggleButton" [ngTemplateOutletContext]="{key: item}"></template>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@ -52,15 +50,9 @@
|
||||
}}</span>
|
||||
<span class="fill-space"></span>
|
||||
</div>
|
||||
<button *ngIf="item.projectId !== zitadelProjectId" matTooltip="{{'ACTIONS.DELETE' | translate}}" color="warn"
|
||||
(click)="deleteProject(item)" class="delete-button" mat-icon-button>
|
||||
<i class="las la-trash"></i>
|
||||
</button>
|
||||
<button matTooltip="{{'ACTIONS.PIN' | translate}}" [ngClass]="{ selected: selection.isSelected(item)}"
|
||||
(click)="selection.toggle(item)" class="edit-button" mat-icon-button>
|
||||
<mat-icon *ngIf="selection.isSelected(item)" svgIcon="mdi_pin"></mat-icon>
|
||||
<mat-icon svgIcon="mdi_pin_outline" *ngIf="!selection.isSelected(item)"></mat-icon>
|
||||
</button>
|
||||
|
||||
<template [ngTemplateOutlet]="deleteButton" [ngTemplateOutletContext]="{key: item}"></template>
|
||||
<template [ngTemplateOutlet]="toggleButton" [ngTemplateOutletContext]="{key: item}"></template>
|
||||
</div>
|
||||
|
||||
<p class="n-items" *ngIf="!loading && items.length === 0">{{'PROJECT.PAGES.NOITEMS' | translate}}</p>
|
||||
@ -72,3 +64,18 @@
|
||||
</div>
|
||||
</ng-template>
|
||||
</div>
|
||||
|
||||
<ng-template #deleteButton let-key="key">
|
||||
<button *ngIf="key.projectId !== zitadelProjectId" matTooltip="{{'ACTIONS.DELETE' | translate}}" color="warn"
|
||||
(click)="deleteProject($event, key)" class="delete-button" mat-icon-button>
|
||||
<i class="las la-trash"></i>
|
||||
</button>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #toggleButton let-key="key">
|
||||
<button matTooltip="{{'ACTIONS.PIN' | translate}}" [ngClass]="{ selected: selection.isSelected(key)}"
|
||||
(click)="toggle(key,$event)" class="edit-button" mat-icon-button>
|
||||
<mat-icon *ngIf="selection.isSelected(key)" svgIcon="mdi_pin"></mat-icon>
|
||||
<mat-icon svgIcon="mdi_pin_outline" *ngIf="!selection.isSelected(key)"></mat-icon>
|
||||
</button>
|
||||
</ng-template>
|
@ -63,11 +63,10 @@ export class OwnedProjectGridComponent implements OnChanges {
|
||||
this.setPrefixedItem('pinned-projects', JSON.stringify(
|
||||
this.selection.selected.map(item => item.projectId),
|
||||
)).then(() => {
|
||||
const filtered = this.notPinned.filter(item => item === selection.added.find(i => i === item));
|
||||
filtered.forEach((f, i) => {
|
||||
this.notPinned.splice(i, 1);
|
||||
selection.added.forEach(item => {
|
||||
const index = this.notPinned.findIndex(i => i.projectId === item.projectId);
|
||||
this.notPinned.splice(index, 1);
|
||||
});
|
||||
|
||||
this.notPinned.push(...selection.removed);
|
||||
});
|
||||
});
|
||||
@ -102,13 +101,6 @@ export class OwnedProjectGridComponent implements OnChanges {
|
||||
}
|
||||
});
|
||||
this.selection.select(...toSelect);
|
||||
|
||||
const toNotPinned: ProjectView.AsObject[] = this.items.filter((item, index) => {
|
||||
if (!array.includes(item.projectId)) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
this.notPinned = toNotPinned;
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -133,7 +125,13 @@ export class OwnedProjectGridComponent implements OnChanges {
|
||||
this.changedView.emit(true);
|
||||
}
|
||||
|
||||
public deleteProject(item: ProjectView.AsObject): void {
|
||||
public toggle(item: ProjectView.AsObject, event: any): void {
|
||||
event.stopPropagation();
|
||||
this.selection.toggle(item);
|
||||
}
|
||||
|
||||
public deleteProject(event: any, item: ProjectView.AsObject): void {
|
||||
event.stopPropagation();
|
||||
const dialogRef = this.dialog.open(WarnDialogComponent, {
|
||||
data: {
|
||||
confirmKey: 'ACTIONS.DELETE',
|
||||
@ -152,6 +150,16 @@ export class OwnedProjectGridComponent implements OnChanges {
|
||||
if (index > -1) {
|
||||
this.items.splice(index, 1);
|
||||
}
|
||||
|
||||
const indexSelection = this.selection.selected.findIndex(iter => iter.projectId === item.projectId);
|
||||
if (indexSelection > -1) {
|
||||
this.selection.selected.splice(indexSelection, 1);
|
||||
}
|
||||
|
||||
const indexPinned = this.notPinned.findIndex(iter => iter.projectId === item.projectId);
|
||||
if (indexPinned > -1) {
|
||||
this.notPinned.splice(indexPinned, 1);
|
||||
}
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
|
@ -4,10 +4,11 @@ import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { MatPaginator, PageEvent } from '@angular/material/paginator';
|
||||
import { MatTableDataSource } from '@angular/material/table';
|
||||
import { Router } from '@angular/router';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb';
|
||||
import { BehaviorSubject, Observable, Subscription } from 'rxjs';
|
||||
import { take } from 'rxjs/operators';
|
||||
import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component';
|
||||
import { ProjectView } from 'src/app/proto/generated/management_pb';
|
||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
@ -59,6 +60,7 @@ export class OwnedProjectListComponent implements OnInit, OnDestroy {
|
||||
public zitadelProjectId: string = '';
|
||||
|
||||
constructor(private router: Router,
|
||||
private route: ActivatedRoute,
|
||||
public translate: TranslateService,
|
||||
private mgmtService: ManagementService,
|
||||
private toast: ToastService,
|
||||
@ -70,7 +72,15 @@ export class OwnedProjectListComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
public ngOnInit(): void {
|
||||
this.getData(10, 0);
|
||||
this.route.queryParams.pipe(take(1)).subscribe(params => {
|
||||
console.log(params);
|
||||
this.getData();
|
||||
if (params.deferredReload) {
|
||||
setTimeout(() => {
|
||||
this.getData();
|
||||
}, 2000);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public ngOnDestroy(): void {
|
||||
@ -97,7 +107,7 @@ export class OwnedProjectListComponent implements OnInit, OnDestroy {
|
||||
this.router.navigate(['/projects', 'create']);
|
||||
}
|
||||
|
||||
private async getData(limit: number, offset: number): Promise<void> {
|
||||
private async getData(limit?: number, offset?: number): Promise<void> {
|
||||
this.loadingSubject.next(true);
|
||||
this.mgmtService.SearchProjects(limit, offset).then(res => {
|
||||
const response = res.toObject();
|
||||
|
@ -68,7 +68,7 @@ export class UserCreateComponent implements OnDestroy {
|
||||
}
|
||||
|
||||
private async loadOrg(): Promise<void> {
|
||||
const domains = (await this.mgmtService.SearchMyOrgDomains(0, 100).then(doms => doms.toObject()));
|
||||
const domains = (await this.mgmtService.SearchMyOrgDomains().then(doms => doms.toObject()));
|
||||
const found = domains.resultList.find(domain => domain.primary);
|
||||
if (found) {
|
||||
this.primaryDomain = found;
|
||||
|
@ -19,7 +19,7 @@
|
||||
|
||||
i {
|
||||
margin-left: 1rem;
|
||||
color: #5282c1;
|
||||
color: var(--color-main);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,7 @@
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
<ng-template #compact>
|
||||
<div class="avatar-circle" matTooltip="Click to show detail">
|
||||
<div class="avatar-circle" matTooltip="Click to show detail" (click)="navigateToObject()" role="button">
|
||||
<div class="membership-avatar">
|
||||
<span style="font-size: 16px;">{{memberships.totalResult}}</span>
|
||||
</div>
|
||||
|
@ -6,13 +6,13 @@
|
||||
</a>
|
||||
<h1>{{user.human ? user.human?.displayName : user.machine?.name}}</h1>
|
||||
|
||||
<span class="fill-space"></span>
|
||||
|
||||
<ng-template appHasRole [appHasRole]="['user.delete$', 'user.delete:'+user?.id]">
|
||||
<button mat-raised-button color="warn" (click)="deleteUser()"><i
|
||||
class="las la-trash"></i>{{'USER.PAGES.DELETE' | translate}}</button>
|
||||
<button mat-icon-button color="warn" matTooltip="{{'USER.PAGES.DELETE' | translate}}"
|
||||
(click)="deleteUser()"><i class="las la-trash"></i></button>
|
||||
</ng-template>
|
||||
|
||||
<span class="fill-space"></span>
|
||||
|
||||
<ng-template appHasRole [appHasRole]="['user.write$', 'user.write:'+user?.id]">
|
||||
<button class="state-button" mat-stroked-button color="warn"
|
||||
*ngIf="user?.state === UserState.USERSTATE_ACTIVE"
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
h1 {
|
||||
margin: 0;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
.fill-space {
|
||||
|
@ -23,7 +23,7 @@
|
||||
|
||||
i {
|
||||
margin-left: 1rem;
|
||||
color: #5282c1;
|
||||
color: var(--color-main);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -125,9 +125,9 @@
|
||||
<ng-container matColumnDef="actions" stickyEnd>
|
||||
<th mat-header-cell *matHeaderCellDef></th>
|
||||
<td mat-cell *matCellDef="let user">
|
||||
<button [disabled]="(['user.delete$', 'user.delete:'+user.id] | hasRole | async) == false"
|
||||
color="warn" mat-icon-button matTooltip="{{'USER.PAGES.DELETE' | translate}}"
|
||||
(click)="deleteUser(user)">
|
||||
<button class="dlt-button"
|
||||
[disabled]="(['user.delete$', 'user.delete:'+user.id] | hasRole | async) == false" color="warn"
|
||||
mat-icon-button matTooltip="{{'USER.PAGES.DELETE' | translate}}" (click)="deleteUser(user)">
|
||||
<i class="las la-trash"></i>
|
||||
</button>
|
||||
</td>
|
||||
|
@ -18,6 +18,26 @@
|
||||
&:last-child {
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.search-button,
|
||||
.dlt-button {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&.search-active {
|
||||
.search-button {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tr {
|
||||
&:hover {
|
||||
.dlt-button {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.selection {
|
||||
@ -27,31 +47,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
tr {
|
||||
button {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
button {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
th {
|
||||
.search-button {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&.search-active {
|
||||
.search-button {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.filtername {
|
||||
margin: 0 1rem;
|
||||
}
|
||||
|
@ -121,6 +121,7 @@ export class UserTableComponent implements OnInit {
|
||||
}
|
||||
|
||||
public applyFilter(event: Event): void {
|
||||
this.selection.clear();
|
||||
const filterValue = (event.target as HTMLInputElement).value;
|
||||
|
||||
this.getData(
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Empty } from 'google-protobuf/google/protobuf/empty_pb';
|
||||
import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
|
||||
import {
|
||||
AddMachineKeyRequest,
|
||||
@ -160,6 +161,9 @@ export type ResponseMapper<TResp, TMappedResp> = (resp: TResp) => TMappedResp;
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class ManagementService {
|
||||
public ownedProjectsCount: BehaviorSubject<number> = new BehaviorSubject(0);
|
||||
public grantedProjectsCount: BehaviorSubject<number> = new BehaviorSubject(0);
|
||||
|
||||
constructor(private readonly grpcService: GrpcService) { }
|
||||
|
||||
public SearchIdps(
|
||||
@ -393,11 +397,9 @@ export class ManagementService {
|
||||
return this.grpcService.mgmt.removeMyOrgDomain(req);
|
||||
}
|
||||
|
||||
public SearchMyOrgDomains(offset: number, limit: number, queryList?: OrgDomainSearchQuery[]):
|
||||
public SearchMyOrgDomains(queryList?: OrgDomainSearchQuery[]):
|
||||
Promise<OrgDomainSearchResponse> {
|
||||
const req: OrgDomainSearchRequest = new OrgDomainSearchRequest();
|
||||
req.setLimit(limit);
|
||||
req.setOffset(offset);
|
||||
if (queryList) {
|
||||
req.setQueriesList(queryList);
|
||||
}
|
||||
@ -834,13 +836,17 @@ export class ManagementService {
|
||||
// USER GRANTS
|
||||
|
||||
public SearchUserGrants(
|
||||
limit: number,
|
||||
offset: number,
|
||||
limit?: number,
|
||||
offset?: number,
|
||||
queryList?: UserGrantSearchQuery[],
|
||||
): Promise<UserGrantSearchResponse> {
|
||||
const req = new UserGrantSearchRequest();
|
||||
req.setLimit(limit);
|
||||
req.setOffset(offset);
|
||||
if (limit) {
|
||||
req.setLimit(limit);
|
||||
}
|
||||
if (offset) {
|
||||
req.setOffset(offset);
|
||||
}
|
||||
if (queryList) {
|
||||
req.setQueriesList(queryList);
|
||||
}
|
||||
@ -929,14 +935,26 @@ export class ManagementService {
|
||||
// project
|
||||
|
||||
public SearchProjects(
|
||||
limit: number, offset: number, queryList?: ProjectSearchQuery[]): Promise<ProjectSearchResponse> {
|
||||
limit?: number, offset?: number, queryList?: ProjectSearchQuery[]): Promise<ProjectSearchResponse> {
|
||||
const req = new ProjectSearchRequest();
|
||||
req.setLimit(limit);
|
||||
req.setOffset(offset);
|
||||
if (limit) {
|
||||
req.setLimit(limit);
|
||||
}
|
||||
if (offset) {
|
||||
req.setOffset(offset);
|
||||
}
|
||||
|
||||
if (queryList) {
|
||||
req.setQueriesList(queryList);
|
||||
}
|
||||
return this.grpcService.mgmt.searchProjects(req);
|
||||
return this.grpcService.mgmt.searchProjects(req).then(value => {
|
||||
const count = value.toObject().resultList.length;
|
||||
if (count >= 0) {
|
||||
this.ownedProjectsCount.next(count);
|
||||
}
|
||||
|
||||
return value;
|
||||
});
|
||||
}
|
||||
|
||||
public SearchGrantedProjects(
|
||||
@ -947,10 +965,12 @@ export class ManagementService {
|
||||
if (queryList) {
|
||||
req.setQueriesList(queryList);
|
||||
}
|
||||
return this.grpcService.mgmt.searchGrantedProjects(req);
|
||||
return this.grpcService.mgmt.searchGrantedProjects(req).then(value => {
|
||||
this.grantedProjectsCount.next(value.toObject().resultList.length);
|
||||
return value;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public GetZitadelDocs(): Promise<ZitadelDocs> {
|
||||
const req = new Empty();
|
||||
return this.grpcService.mgmt.getZitadelDocs(req);
|
||||
@ -972,7 +992,11 @@ export class ManagementService {
|
||||
public CreateProject(project: ProjectCreateRequest.AsObject): Promise<Project> {
|
||||
const req = new ProjectCreateRequest();
|
||||
req.setName(project.name);
|
||||
return this.grpcService.mgmt.createProject(req);
|
||||
return this.grpcService.mgmt.createProject(req).then(value => {
|
||||
const current = this.ownedProjectsCount.getValue();
|
||||
this.ownedProjectsCount.next(current + 1);
|
||||
return value;
|
||||
});
|
||||
}
|
||||
|
||||
public UpdateProject(id: string, projectView: ProjectView.AsObject): Promise<Project> {
|
||||
@ -1221,7 +1245,11 @@ export class ManagementService {
|
||||
public RemoveProject(id: string): Promise<Empty> {
|
||||
const req = new ProjectID();
|
||||
req.setId(id);
|
||||
return this.grpcService.mgmt.removeProject(req);
|
||||
return this.grpcService.mgmt.removeProject(req).then(value => {
|
||||
const current = this.ownedProjectsCount.getValue();
|
||||
this.ownedProjectsCount.next(current > 0 ? current - 1 : 0);
|
||||
return value;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
@ -5,14 +5,26 @@
|
||||
"SECURITYANDPRIVACY": "Datenschutz und Personalisierung",
|
||||
"SECURITYANDPRIVACY_DESC": "Verwalte Deine Informationen und Sicherheitseinstellungen.",
|
||||
"SECURITYANDPRIVACY_BUTTON": "Daten verwalten und personalisieren",
|
||||
"CHANGE_PWD":"Password ändern",
|
||||
"PROTECTION": "Organisationsrichtlinien",
|
||||
"PROTECTION_DESC": "Verwalte Deine Organisationsrichtlinien und entdecke die vorgefertigte Lösungen, die Zeit sparen und Sicherheit gewährleisten.",
|
||||
"PROTECTION_BUTTON": "Erkunden",
|
||||
"PROJECTS": "Volle Skalierbarkeit und Anpassungsfähigkeit",
|
||||
"PROJECTS_DESC": "Autorisiere andere Benutzer, Deine eigenen Projekte zu verwenden, oder erstelle benutzerdefinierte Rollen für berechtigte Projekte",
|
||||
"PROJECTS_BUTTON": "Projektübersicht",
|
||||
"PROJECTS_NEW_LINK":"Neues Projekt erstellen",
|
||||
"IAM_CREATE_ORG":"Organisation erstellen",
|
||||
"ORG_POLICY_COMPLEXITY":"Passwort Komplexität",
|
||||
"ORG_POLICY_IAM":"Organisation Zugangseinstellungen",
|
||||
"ORG_POLICY_LOGIN":"Login Richtlinie",
|
||||
"IAM_POLICY_COMPLEXITY":"Systemweite Passwort Komplexität",
|
||||
"IAM_POLICY_IAM":"Systemweite Zugangseinstellungen",
|
||||
"IAN_POLICY_LOGIN":"Systemweite Login Richtlinie",
|
||||
"USERS": "Erstelle und verwalte Deine Benutzer.",
|
||||
"USERS_DESC": "Überwache Dein Rollenkonzept in Echtzeit. Ergreife sofort Massnahmen.",
|
||||
"USERS_HUMANS":"Zeige Benutzer",
|
||||
"USERS_MACHINES": "Zeige Service User",
|
||||
"USERS_CREATE":"User erstellen",
|
||||
"USERS_BUTTON": "Benutzer anzeigen",
|
||||
"IAM": "Identity- und Access-Management",
|
||||
"IAM_DESC": "Verwalte Deine Organisationen und Administratoren.",
|
||||
@ -26,6 +38,7 @@
|
||||
"PERSONAL_INFO": "Persönliche Informationen",
|
||||
"IAM":"Administration",
|
||||
"ORGANIZATION": "Organisation",
|
||||
"ADMINSECTION":"Administration",
|
||||
"PROJECTSSECTION":"Projektsektion",
|
||||
"PROJECT": "Projekte",
|
||||
"GRANTEDPROJECT":"Berechtigte Projekte",
|
||||
@ -106,8 +119,11 @@
|
||||
"1":"Nach Username filtern",
|
||||
"2":"Nach Vornamen filtern",
|
||||
"3":"Nach Nachnamen filtern",
|
||||
"4":"Nach rollenschlüssel filtern",
|
||||
"5":"Nach Display Namen filtern",
|
||||
"6":"Nach Email filtern"
|
||||
"6":"Nach Email filtern",
|
||||
"10":"Nach Organisationsname filtern",
|
||||
"12":"Project Name"
|
||||
}
|
||||
},
|
||||
"MFA": {
|
||||
@ -384,6 +400,7 @@
|
||||
"VERIFICATION_NEWTOKEN_DESC":"Wenn Du ein neues Token anfordern willst, klicke auf die gewünschte Methode. Wenn Du ein vorhandenes Token validieren möchtest, klicke auf \"Verifizieren\".",
|
||||
"VERIFICATION_VALIDATION_ONGOING":"Ein Token zur Validierung wurde bereits angefragt. Klicke auf \"Verifizieren\", um dieses Token zu validieren.",
|
||||
"VERIFICATION_VALIDATION_ONGOING_TYPE":"Typ des Tokens:",
|
||||
"VERIFICATION_SUCCESSFUL":"Domain erfolgreich validiert!",
|
||||
"REQUESTNEWTOKEN":"Neues Token anfordern",
|
||||
"TYPES": {
|
||||
"1":"HTTP",
|
||||
|
@ -5,14 +5,26 @@
|
||||
"SECURITYANDPRIVACY": "Data Protection and Personalisation",
|
||||
"SECURITYANDPRIVACY_DESC": "Manage Your Information and Security Settings",
|
||||
"SECURITYANDPRIVACY_BUTTON": "Personalise Information and Security",
|
||||
"CHANGE_PWD":"Change Password",
|
||||
"PROTECTION": "Organisational Policies",
|
||||
"PROTECTION_DESC": "Manage your organisational guidelines. Explore some pre-packaged solutions that save you time and ensure security.",
|
||||
"PROTECTION_BUTTON": "Explore",
|
||||
"PROJECTS": "Create and Manage Your Applications and Projects",
|
||||
"PROJECTS_DESC": "Authorize others to use your projects or define custom roles on eligible projects.",
|
||||
"PROJECTS_BUTTON": "Project Overview",
|
||||
"PROJECTS_NEW_LINK":"Create new project",
|
||||
"ORG_POLICY_COMPLEXITY":"Password Complexity Settings",
|
||||
"ORG_POLICY_IAM":"Organisation Access Properties",
|
||||
"ORG_POLICY_LOGIN":"Login Policy",
|
||||
"IAM_CREATE_ORG":"Create organisation",
|
||||
"IAM_POLICY_COMPLEXITY":"System Password Complexity Settings",
|
||||
"IAM_POLICY_IAM":"System Access Properties",
|
||||
"IAM_POLICY_LOGIN":"System Login Policy",
|
||||
"USERS": "Create and Manage Your Users",
|
||||
"USERS_DESC": "Monitor your role concept in real time. Take immediate action.",
|
||||
"USERS_HUMANS":"Show human users",
|
||||
"USERS_MACHINES": "Show machine users",
|
||||
"USERS_CREATE":"Create User",
|
||||
"USERS_BUTTON": "Show Users",
|
||||
"IAM": "Identity and Access Management",
|
||||
"IAM_DESC": "Manage your organisations and administrators.",
|
||||
@ -26,6 +38,7 @@
|
||||
"PERSONAL_INFO": "Personal Information",
|
||||
"IAM":"Administration",
|
||||
"ORGANIZATION": "Organisation",
|
||||
"ADMINSECTION":"Administration",
|
||||
"PROJECTSSECTION":"Projects Section",
|
||||
"PROJECT": "Projects",
|
||||
"GRANTEDPROJECT":"Granted Projects",
|
||||
@ -106,8 +119,11 @@
|
||||
"1":"Filter for Username",
|
||||
"2":"filter for Firstname",
|
||||
"3":"filter for Lastname",
|
||||
"4":"filter for role Key",
|
||||
"5":"filter for DisplayName",
|
||||
"6":"filter for email"
|
||||
"6":"filter for email",
|
||||
"10":"filter for organisation name",
|
||||
"12":"filter for project name"
|
||||
}
|
||||
},
|
||||
"MFA": {
|
||||
@ -384,6 +400,7 @@
|
||||
"VERIFICATION_NEWTOKEN_DESC":"If you want to request a new token, select you preferred method. If you want to validate a persisting token, click the button above.",
|
||||
"VERIFICATION_VALIDATION_ONGOING":"A verification token has already been requested. Click on the button to trigger a verification check.",
|
||||
"VERIFICATION_VALIDATION_ONGOING_TYPE":"Type of the token:",
|
||||
"VERIFICATION_SUCCESSFUL":"Domain successfully verified!",
|
||||
"REQUESTNEWTOKEN":"Request new Token",
|
||||
"TYPES": {
|
||||
"1":"HTTP",
|
||||
|
@ -1,5 +1,6 @@
|
||||
@import 'src/app/modules/card/card';
|
||||
@import './styles/table';
|
||||
@import './styles/link.scss';
|
||||
@import './styles/sidenav-list';
|
||||
@import 'src/app/modules/avatar/avatar.component';
|
||||
@import 'src/app/modules/changes/changes.component';
|
||||
@ -22,4 +23,5 @@
|
||||
@include meta-theme($theme);
|
||||
@include theme-card($theme);
|
||||
@include textvar($theme);
|
||||
@include link-theme($theme);
|
||||
}
|
||||
|
@ -61,20 +61,20 @@ $caos-dark-brand: (
|
||||
);
|
||||
|
||||
$caos-light-brand: (
|
||||
50: #fff,
|
||||
100: #dde6f3,
|
||||
200: #b4c9e4,
|
||||
300: #7fa3d1,
|
||||
400: #6992c9,
|
||||
500: #5282c1,
|
||||
600: #4072b4,
|
||||
700: #38649d,
|
||||
800: #305687,
|
||||
900: #284770,
|
||||
A100: #fff,
|
||||
A200: #dde6f3,
|
||||
A300: #6992c9,
|
||||
A400: #38649d,
|
||||
50: #eaedfa,
|
||||
100: #ccd2f2,
|
||||
200: #aab4ea,
|
||||
300: #8796e1,
|
||||
400: #6e80da,
|
||||
500: #5469d4,
|
||||
600: #4d61cf,
|
||||
700: #4356c9,
|
||||
800: #3a4cc3,
|
||||
900: #293bb9,
|
||||
A100: #f9faff,
|
||||
A200: #c6ccff,
|
||||
A300: #939fff,
|
||||
A400: #7a88ff,
|
||||
A500:#333,
|
||||
A600: #000,
|
||||
A700: #8795a1,
|
||||
@ -167,7 +167,9 @@ $custom-typography:
|
||||
@include component-themes($light-theme);
|
||||
@include angular-material-theme($light-theme);
|
||||
|
||||
--table-row-back: #eceef1;
|
||||
--grey: #697386;
|
||||
--table-row-back: #e7ebf0;
|
||||
--color-main: #5469d4;
|
||||
|
||||
.sidenav,
|
||||
.main-container,
|
||||
@ -194,13 +196,18 @@ $custom-typography:
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.root-header {
|
||||
box-shadow: inset 0 -1px #e3e8ee;
|
||||
}
|
||||
}
|
||||
|
||||
.dark-theme {
|
||||
@include component-themes($dark-theme);
|
||||
@include angular-material-theme($dark-theme);
|
||||
|
||||
--table-row-back: #363738;
|
||||
--table-row-back: #292a2b;
|
||||
--color-main: #5282c1;
|
||||
|
||||
.sidenav,
|
||||
.main-container,
|
||||
@ -227,6 +234,10 @@ $custom-typography:
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.root-header {
|
||||
box-shadow: inset 0 -1px #303131;
|
||||
}
|
||||
}
|
||||
// @include mat-checkbox-theme($candy-app-theme);
|
||||
|
||||
|
16
console/src/styles/link.scss
Normal file
16
console/src/styles/link.scss
Normal file
@ -0,0 +1,16 @@
|
||||
@import '~@angular/material/theming';
|
||||
|
||||
@mixin link-theme($theme) {
|
||||
/* stylelint-disable */
|
||||
$primary: map-get($theme, primary);
|
||||
$primary-color: mat-color($primary, 500);
|
||||
$primary-color-lighter: mat-color($primary, A300);
|
||||
|
||||
a {
|
||||
color: $primary-color;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: $primary-color-lighter;
|
||||
}
|
||||
}
|
@ -7,11 +7,12 @@
|
||||
$primary-color: mat-color($primary, 500);
|
||||
$accent-color: mat-color($accent, 500);
|
||||
$primary-dark: mat-color($primary, A900);
|
||||
$foreground: map-get($theme, foreground);
|
||||
$sec-dark: mat-color($primary, A800);
|
||||
/* stylelint-enable */
|
||||
|
||||
.nav-item {
|
||||
color: inherit;
|
||||
color: mat-color($foreground, text) !important;
|
||||
|
||||
&:hover {
|
||||
background-color: $sec-dark;
|
||||
@ -48,27 +49,62 @@
|
||||
}
|
||||
|
||||
.root-header {
|
||||
box-shadow: 0 5px 10px rgba(0, 0, 0, .12);
|
||||
box-shadow: inset 0 -1px #e3e8ee;
|
||||
background-color: $primary-dark !important;
|
||||
transition: background-color .3s cubic-bezier(.645, .045, .355, 1);
|
||||
}
|
||||
|
||||
.admin-line {
|
||||
background: $accent-color;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: calc(100vw - 300px);
|
||||
background-color: $primary-color;
|
||||
color: white;
|
||||
margin-right: 1rem;
|
||||
border-top-right-radius: 50vw;
|
||||
border-bottom-right-radius: 50vw;
|
||||
z-index: 1;
|
||||
font-size: 13px;
|
||||
padding: 3px 2rem;
|
||||
transform: translateY(75%);
|
||||
transition: all .2s;
|
||||
border-top-right-radius: 5px;
|
||||
|
||||
span {
|
||||
display: none;
|
||||
}
|
||||
|
||||
button {
|
||||
height: 1.2rem;
|
||||
width: 1.2rem;
|
||||
line-height: 1.2rem;
|
||||
margin-right: 1rem;
|
||||
|
||||
* {
|
||||
height: 1.2rem;
|
||||
width: 1.2rem;
|
||||
line-height: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 0;
|
||||
bottom: 0;
|
||||
height: 0;
|
||||
top: 0;
|
||||
left: 0;
|
||||
border-bottom: 20px solid $primary-dark;
|
||||
border-right: 20px solid transparent;
|
||||
border-bottom: 20px solid transparent;
|
||||
border-left: 20px solid $primary-dark;
|
||||
transition: border-color .3s cubic-bezier(.645, .045, .355, 1);
|
||||
}
|
||||
|
||||
&.expanded,
|
||||
&:hover {
|
||||
transform: translateY(0%);
|
||||
right: 0;
|
||||
|
||||
span {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +36,7 @@
|
||||
|
||||
&:hover {
|
||||
td {
|
||||
background-color: var(--table-row-back); // rgba($inv-color, .05);
|
||||
background: var(--table-row-back); // rgba($inv-color, .05);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user