mirror of
https://github.com/zitadel/zitadel.git
synced 2025-05-07 12:56:47 +00:00
fix(console): migrate from tslint to eslint, project delete from table (#2490)
* es lint * modify tsconfig, auto lint some stuff * lint * lint * lint * lint * html ts lint * lint * lint, tsconfig * fix project delete, state table * eslint config, remove cnslHint directive * mfa selector, info row fixes * lint * fix login policy, granted orgs table state, lint Co-authored-by: Florian Forster <florian@caos.ch>
This commit is contained in:
parent
bdf63800f7
commit
b1caef81da
53
console/.eslintrc.json
Normal file
53
console/.eslintrc.json
Normal file
@ -0,0 +1,53 @@
|
||||
{
|
||||
"root": true,
|
||||
"ignorePatterns": [
|
||||
"projects/**/*"
|
||||
],
|
||||
"overrides": [
|
||||
{
|
||||
"files": [
|
||||
"*.ts"
|
||||
],
|
||||
"parserOptions": {
|
||||
"project": [
|
||||
"tsconfig.json",
|
||||
"e2e/tsconfig.json"
|
||||
],
|
||||
"createDefaultProgram": true
|
||||
},
|
||||
"extends": [
|
||||
"plugin:@angular-eslint/recommended",
|
||||
"plugin:@angular-eslint/template/process-inline-templates"
|
||||
],
|
||||
"rules": {
|
||||
"@angular-eslint/no-conflicting-lifecycle": "off",
|
||||
"@angular-eslint/no-host-metadata-property": "off",
|
||||
"@angular-eslint/component-selector": [
|
||||
"error",
|
||||
{
|
||||
"prefix": "cnsl",
|
||||
"style": "kebab-case",
|
||||
"type": "element"
|
||||
}
|
||||
],
|
||||
"@angular-eslint/directive-selector": [
|
||||
"error",
|
||||
{
|
||||
"prefix": "cnsl",
|
||||
"style": "camelCase",
|
||||
"type": "attribute"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": [
|
||||
"*.html"
|
||||
],
|
||||
"extends": [
|
||||
"plugin:@angular-eslint/template/recommended"
|
||||
],
|
||||
"rules": {}
|
||||
}
|
||||
]
|
||||
}
|
@ -28,19 +28,19 @@
|
||||
"src/manifest.webmanifest"
|
||||
],
|
||||
"styles": [
|
||||
"src/styles.scss"
|
||||
"src/styles.scss"
|
||||
],
|
||||
"scripts": [
|
||||
"./node_modules/tinycolor2/dist/tinycolor-min.js"
|
||||
],
|
||||
"allowedCommonJsDependencies": [
|
||||
"@angular/common/locales/de",
|
||||
"src/app/proto/generated/zitadel/admin_pb",
|
||||
"src/app/proto/generated/zitadel/management_pb",
|
||||
"src/app/proto/generated/**",
|
||||
"file-saver",
|
||||
"qrcode"
|
||||
],
|
||||
"@angular/common/locales/de",
|
||||
"src/app/proto/generated/zitadel/admin_pb",
|
||||
"src/app/proto/generated/zitadel/management_pb",
|
||||
"src/app/proto/generated/**",
|
||||
"file-saver",
|
||||
"qrcode"
|
||||
],
|
||||
"vendorChunk": true,
|
||||
"extractLicenses": false,
|
||||
"buildOptimizer": false,
|
||||
@ -84,8 +84,8 @@
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "4mb",
|
||||
"maximumError": "5mb"
|
||||
"maximumWarning": "5mb",
|
||||
"maximumError": "6mb"
|
||||
},
|
||||
{
|
||||
"type": "anyComponentStyle",
|
||||
@ -102,8 +102,7 @@
|
||||
},
|
||||
"serve": {
|
||||
"builder": "@angular-devkit/build-angular:dev-server",
|
||||
"options": {
|
||||
},
|
||||
"options": {},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"browserTarget": "console:build:production"
|
||||
@ -142,15 +141,11 @@
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"builder": "@angular-eslint/builder:lint",
|
||||
"options": {
|
||||
"tsConfig": [
|
||||
"tsconfig.app.json",
|
||||
"tsconfig.spec.json"
|
||||
],
|
||||
"exclude": [
|
||||
"**/node_modules/**",
|
||||
"**/proto/generated/**"
|
||||
"lintFilePatterns": [
|
||||
"src/**/*.ts",
|
||||
"src/**/*.html"
|
||||
]
|
||||
}
|
||||
},
|
||||
@ -174,6 +169,7 @@
|
||||
},
|
||||
"defaultProject": "console",
|
||||
"cli": {
|
||||
"analytics": "2b4e8e6c-f053-4562-b7a6-00c6c06a6791"
|
||||
"analytics": "2b4e8e6c-f053-4562-b7a6-00c6c06a6791",
|
||||
"defaultCollection": "@angular-eslint/schematics"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,6 @@ export class AppPage {
|
||||
}
|
||||
|
||||
getTitleText() {
|
||||
return element(by.css('app-root .content span')).getText() as Promise<string>;
|
||||
return element(by.css('cnsl-root .content span')).getText() as Promise<string>;
|
||||
}
|
||||
}
|
||||
|
2222
console/package-lock.json
generated
2222
console/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -48,6 +48,14 @@
|
||||
"zone.js": "~0.11.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-eslint/builder": "12.5.0",
|
||||
"@angular-eslint/eslint-plugin": "12.5.0",
|
||||
"@angular-eslint/eslint-plugin-template": "12.5.0",
|
||||
"@angular-eslint/schematics": "12.5.0",
|
||||
"@angular-eslint/template-parser": "12.5.0",
|
||||
"@typescript-eslint/eslint-plugin": "4.28.2",
|
||||
"@typescript-eslint/parser": "4.28.2",
|
||||
"eslint": "^7.26.0",
|
||||
"@angular-devkit/build-angular": "~12.2.8",
|
||||
"@angular/cli": "~12.2.8",
|
||||
"@angular/compiler-cli": "~12.2.8",
|
||||
@ -69,7 +77,6 @@
|
||||
"stylelint-config-standard": "^22.0.0",
|
||||
"stylelint-scss": "^3.21.0",
|
||||
"ts-node": "~10.2.1",
|
||||
"tslint": "~6.1.3",
|
||||
"typescript": "^4.2.4"
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
<ng-container *ngIf="(authService.user | async) || {} as user">
|
||||
<ng-container *ngIf="$any(authService.user | async) || {} as user">
|
||||
<ng-container *ngIf="((['iam.read$','iam.write$'] | hasRole)) as iamuser$">
|
||||
<mat-toolbar class="root-header">
|
||||
<button *ngIf="authenticationService.authenticated" aria-label="Toggle sidenav" mat-icon-button
|
||||
@ -7,7 +7,7 @@
|
||||
</button>
|
||||
<ng-container *ngIf="labelpolicy && !labelpolicy?.disableWatermark">
|
||||
<a class="title" [routerLink]="['/']">
|
||||
<img class="logo" alt="zitadel logo" *ngIf="componentCssClass == 'dark-theme'; else lighttheme"
|
||||
<img class="logo" alt="zitadel logo" *ngIf="componentCssClass === 'dark-theme'; else lighttheme"
|
||||
src="../assets/images/zitadel-logo-solo-light.svg" />
|
||||
<ng-template #lighttheme>
|
||||
<img alt="zitadel logo" class="logo" src="../assets/images/zitadel-logo-solo-dark.svg" />
|
||||
@ -48,7 +48,7 @@
|
||||
<button class="show-all" mat-menu-item [routerLink]="[ '/org/overview' ]">{{'MENU.SHOWORGS' |
|
||||
translate}}</button>
|
||||
|
||||
<ng-template appHasRole [appHasRole]="['org.create','iam.write']">
|
||||
<ng-template cnslHasRole [hasRole]="['org.create','iam.write']">
|
||||
<button mat-menu-item [routerLink]="[ '/org/create' ]">
|
||||
<mat-icon class="avatar">add</mat-icon>
|
||||
{{'MENU.NEWORG' | translate}}
|
||||
@ -60,20 +60,20 @@
|
||||
<a class="doc-link" href="https://docs.zitadel.ch" mat-stroked-button target="_blank">{{'MENU.DOCUMENTATION'
|
||||
| translate}}</a>
|
||||
<div (clickOutside)="closeAccountCard()" class="icon-container">
|
||||
<app-avatar
|
||||
<cnsl-avatar
|
||||
*ngIf="user && (user.human?.profile?.displayName || (user.human?.profile?.firstName && user.human?.profile?.lastName))"
|
||||
class="avatar dontcloseonclick" (click)="showAccount = !showAccount" [active]="showAccount" [avatarUrl]="user.human?.profile?.avatarUrl || ''" [forColor]="user?.preferredLoginName"
|
||||
[name]="user.human.profile.displayName ? user.human.profile.displayName : (user.human.profile.firstName + ' '+ user.human.profile.lastName)"
|
||||
[size]="38">
|
||||
</app-avatar>
|
||||
<app-accounts-card @accounts class="a_card mat-elevation-z1" *ngIf="showAccount"
|
||||
(close)="showAccount = false" [user]="user" [iamuser]="iamuser$ | async">
|
||||
</app-accounts-card>
|
||||
</cnsl-avatar>
|
||||
<cnsl-accounts-card @accounts class="a_card mat-elevation-z1" *ngIf="showAccount"
|
||||
(closedCard)="showAccount = false" [user]="user" [iamuser]="iamuser$ | async">
|
||||
</cnsl-accounts-card>
|
||||
</div>
|
||||
</mat-toolbar>
|
||||
<mat-drawer-container class="main-container">
|
||||
<mat-drawer #drawer class="sidenav" [mode]="(isHandset$ | async) ? 'over' : 'side'"
|
||||
[opened]="!(isHandset$ | async) && authenticationService.authenticated">
|
||||
[opened]="(isHandset$ | async) === false && authenticationService.authenticated">
|
||||
<div class="side-column">
|
||||
<div class="list">
|
||||
<a @navitem class="nav-item" [routerLinkActive]="['active']"
|
||||
@ -92,8 +92,7 @@
|
||||
</ng-container>
|
||||
|
||||
<div *ngIf="org" [@navAnimation]="org">
|
||||
<ng-template appHasRole
|
||||
[appHasRole]="['org.read']">
|
||||
<ng-template cnslHasRole [hasRole]="['org.read']">
|
||||
<div @navitem class="divider">
|
||||
<div class="line"></div>
|
||||
<span>{{org?.name ? org.name : ('MENU.ORGSECTION' | translate)}}</span>
|
||||
@ -101,7 +100,7 @@
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<ng-template appHasRole [appHasRole]="['org.read']">
|
||||
<ng-template cnslHasRole [hasRole]="['org.read']">
|
||||
<a @navitem matTooltip="{{'MENU.TOOLTIP.ORG' | translate}}" class="nav-item"
|
||||
[routerLinkActive]="['active']" [routerLink]="[ '/org']">
|
||||
<i class="icon las la-cog"></i>
|
||||
@ -109,7 +108,7 @@
|
||||
</a>
|
||||
</ng-template>
|
||||
|
||||
<ng-template appHasRole [appHasRole]="['project.read(:[0-9]*)?']">
|
||||
<ng-template cnslHasRole [hasRole]="['project.read(:[0-9]*)?']">
|
||||
<a @navitem matTooltip="{{'MENU.TOOLTIP.SELFPROJECTS' | translate}}" class="nav-item"
|
||||
[routerLinkActive]="['active']" [routerLink]="[ '/projects']">
|
||||
<i class="icon las la-layer-group"></i>
|
||||
@ -133,7 +132,7 @@
|
||||
</a>
|
||||
</ng-template>
|
||||
|
||||
<ng-template appHasRole [appHasRole]="['user.read(:[0-9]*)?']">
|
||||
<ng-template cnslHasRole [hasRole]="['user.read(:[0-9]*)?']">
|
||||
<a @navitem matTooltip="{{'MENU.TOOLTIP.HUMANUSERS' | translate}}" class="nav-item"
|
||||
[routerLinkActive]="['active']" [routerLink]="[ '/users/list/humans']"
|
||||
[routerLinkActiveOptions]="{ exact: true }">
|
||||
@ -149,7 +148,7 @@
|
||||
</a>
|
||||
</ng-template>
|
||||
|
||||
<ng-template appHasRole [appHasRole]="['user.grant.read(:[0-9]*)?']">
|
||||
<ng-template cnslHasRole [hasRole]="['user.grant.read(:[0-9]*)?']">
|
||||
<a @navitem matTooltip="{{'MENU.TOOLTIP.AUTHZ' | translate}}" class="nav-item"
|
||||
[routerLinkActive]="['active']" [routerLink]="[ '/grants']"
|
||||
[routerLinkActiveOptions]="{ exact: true }">
|
||||
|
@ -15,7 +15,6 @@ import { accountCard, adminLineAnimation, navAnimations, routeAnimations, toolba
|
||||
import { TextQueryMethod } from './proto/generated/zitadel/object_pb';
|
||||
import { Org, OrgNameQuery, OrgQuery } from './proto/generated/zitadel/org_pb';
|
||||
import { LabelPolicy, PrivacyPolicy } from './proto/generated/zitadel/policy_pb';
|
||||
import { User } from './proto/generated/zitadel/user_pb';
|
||||
import { AuthenticationService } from './services/authentication.service';
|
||||
import { GrpcAuthService } from './services/grpc-auth.service';
|
||||
import { ManagementService } from './services/mgmt.service';
|
||||
@ -24,7 +23,7 @@ import { UpdateService } from './services/update.service';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
selector: 'cnsl-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.scss'],
|
||||
animations: [
|
||||
@ -48,7 +47,7 @@ export class AppComponent implements OnDestroy {
|
||||
public showAccount: boolean = false;
|
||||
public org!: Org.AsObject;
|
||||
public orgs$: Observable<Org.AsObject[]> = of([]);
|
||||
public user!: User.AsObject;
|
||||
// public user!: User.AsObject;
|
||||
public isDarkTheme: Observable<boolean> = of(true);
|
||||
|
||||
public orgLoading$: BehaviorSubject<any> = new BehaviorSubject(false);
|
||||
@ -350,7 +349,7 @@ export class AppComponent implements OnDestroy {
|
||||
|
||||
this.authService.user.subscribe(userprofile => {
|
||||
if (userprofile) {
|
||||
this.user = userprofile;
|
||||
// this.user = userprofile;
|
||||
const cropped = navigator.language.split('-')[0] ?? 'en';
|
||||
const fallbackLang = cropped.match(/en|de/) ? cropped : 'en';
|
||||
|
||||
|
@ -1,31 +1,31 @@
|
||||
import { Directive, EventEmitter, HostListener, Input, Output } from '@angular/core';
|
||||
|
||||
@Directive({
|
||||
selector: '[appCopyToClipboard]',
|
||||
selector: '[cnslCopyToClipboard]',
|
||||
})
|
||||
export class CopyToClipboardDirective {
|
||||
@Input() valueToCopy: string = '';
|
||||
@Output() copiedValue: EventEmitter<string> = new EventEmitter();
|
||||
@Input() valueToCopy: string = '';
|
||||
@Output() copiedValue: EventEmitter<string> = new EventEmitter();
|
||||
|
||||
@HostListener('click', ['$event.target']) onMouseEnter(): void {
|
||||
this.copytoclipboard(this.valueToCopy);
|
||||
}
|
||||
@HostListener('click', ['$event.target']) onMouseEnter(): void {
|
||||
this.copytoclipboard(this.valueToCopy);
|
||||
}
|
||||
|
||||
public copytoclipboard(value: string): void {
|
||||
const selBox = document.createElement('textarea');
|
||||
selBox.style.position = 'fixed';
|
||||
selBox.style.left = '0';
|
||||
selBox.style.top = '0';
|
||||
selBox.style.opacity = '0';
|
||||
selBox.value = value;
|
||||
document.body.appendChild(selBox);
|
||||
selBox.focus();
|
||||
selBox.select();
|
||||
document.execCommand('copy');
|
||||
document.body.removeChild(selBox);
|
||||
this.copiedValue.emit(value);
|
||||
setTimeout(() => {
|
||||
this.copiedValue.emit('');
|
||||
}, 3000);
|
||||
}
|
||||
public copytoclipboard(value: string): void {
|
||||
const selBox = document.createElement('textarea');
|
||||
selBox.style.position = 'fixed';
|
||||
selBox.style.left = '0';
|
||||
selBox.style.top = '0';
|
||||
selBox.style.opacity = '0';
|
||||
selBox.value = value;
|
||||
document.body.appendChild(selBox);
|
||||
selBox.focus();
|
||||
selBox.select();
|
||||
document.execCommand('copy');
|
||||
document.body.removeChild(selBox);
|
||||
this.copiedValue.emit(value);
|
||||
setTimeout(() => {
|
||||
this.copiedValue.emit('');
|
||||
}, 3000);
|
||||
}
|
||||
}
|
||||
|
@ -3,12 +3,12 @@ import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
|
||||
|
||||
|
||||
@Directive({
|
||||
selector: '[appHasFeature]',
|
||||
selector: '[cnslHasFeature]',
|
||||
})
|
||||
|
||||
export class HasFeatureDirective {
|
||||
private hasView: boolean = false;
|
||||
@Input() public set appHasFeature(features: string[] | RegExp[]) {
|
||||
@Input() public set hasFeature(features: string[] | RegExp[]) {
|
||||
if (features && features.length > 0) {
|
||||
this.authService.canUseFeature(features).subscribe(isAllowed => {
|
||||
if (isAllowed && !this.hasView) {
|
||||
|
@ -3,28 +3,28 @@ import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
|
||||
|
||||
|
||||
@Directive({
|
||||
selector: '[appHasRole]',
|
||||
selector: '[cnslHasRole]',
|
||||
})
|
||||
|
||||
export class HasRoleDirective {
|
||||
private hasView: boolean = false;
|
||||
@Input() public set appHasRole(roles: string[] | RegExp[]) {
|
||||
if (roles && roles.length > 0) {
|
||||
this.authService.isAllowed(roles).subscribe(isAllowed => {
|
||||
if (isAllowed && !this.hasView) {
|
||||
this.viewContainerRef.clear();
|
||||
this.viewContainerRef.createEmbeddedView(this.templateRef);
|
||||
} else if (this.hasView) {
|
||||
this.viewContainerRef.clear();
|
||||
this.hasView = false;
|
||||
}
|
||||
});
|
||||
private hasView: boolean = false;
|
||||
@Input() public set hasRole(roles: string[] | RegExp[]) {
|
||||
if (roles && roles.length > 0) {
|
||||
this.authService.isAllowed(roles).subscribe(isAllowed => {
|
||||
if (isAllowed && !this.hasView) {
|
||||
this.viewContainerRef.clear();
|
||||
this.viewContainerRef.createEmbeddedView(this.templateRef);
|
||||
} else if (this.hasView) {
|
||||
this.viewContainerRef.clear();
|
||||
this.hasView = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
constructor(
|
||||
private authService: GrpcAuthService,
|
||||
protected templateRef: TemplateRef<any>,
|
||||
protected viewContainerRef: ViewContainerRef,
|
||||
) { }
|
||||
constructor(
|
||||
private authService: GrpcAuthService,
|
||||
protected templateRef: TemplateRef<any>,
|
||||
protected viewContainerRef: ViewContainerRef,
|
||||
) { }
|
||||
}
|
||||
|
@ -1,17 +1,17 @@
|
||||
import { Directive, ElementRef, EventEmitter, HostListener, Output } from '@angular/core';
|
||||
|
||||
@Directive({
|
||||
selector: '[appOutsideClick]',
|
||||
selector: '[cnslOutsideClick]',
|
||||
})
|
||||
export class OutsideClickDirective {
|
||||
constructor(private elementRef: ElementRef) { }
|
||||
constructor(private elementRef: ElementRef) { }
|
||||
|
||||
@Output() public clickOutside: EventEmitter<HTMLElement> = new EventEmitter();
|
||||
@Output() public clickOutside: EventEmitter<HTMLElement> = new EventEmitter();
|
||||
|
||||
@HostListener('document:click', ['$event.target']) onMouseEnter(targetElement: HTMLElement): void {
|
||||
const clickedInside = this.elementRef.nativeElement.contains(targetElement);
|
||||
if (!clickedInside) {
|
||||
this.clickOutside.emit(targetElement);
|
||||
}
|
||||
@HostListener('document:click', ['$event.target']) onMouseEnter(targetElement: HTMLElement): void {
|
||||
const clickedInside = this.elementRef.nativeElement.contains(targetElement);
|
||||
if (!clickedInside) {
|
||||
this.clickOutside.emit(targetElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,32 +1,32 @@
|
||||
import { Directive, ElementRef, EventEmitter, HostListener, Output } from '@angular/core';
|
||||
|
||||
@Directive({
|
||||
selector: '[appScrollable]',
|
||||
selector: '[cnslScrollable]',
|
||||
})
|
||||
export class ScrollableDirective {
|
||||
// when using this directive, add overflow-y scroll to css
|
||||
@Output() scrollPosition: EventEmitter<any> = new EventEmitter();
|
||||
// when using this directive, add overflow-y scroll to css
|
||||
@Output() scrollPosition: EventEmitter<any> = new EventEmitter();
|
||||
|
||||
constructor(public el: ElementRef) { }
|
||||
constructor(public el: ElementRef) { }
|
||||
|
||||
@HostListener('scroll', ['$event'])
|
||||
public onScroll(event: any): void {
|
||||
try {
|
||||
const top = event.target.scrollTop;
|
||||
const height = this.el.nativeElement.scrollHeight;
|
||||
const offset = this.el.nativeElement.offsetHeight;
|
||||
@HostListener('scroll', ['$event'])
|
||||
public onScroll(event: any): void {
|
||||
try {
|
||||
const top = event.target.scrollTop;
|
||||
const height = this.el.nativeElement.scrollHeight;
|
||||
const offset = this.el.nativeElement.offsetHeight;
|
||||
|
||||
// emit bottom event
|
||||
if (top > height - offset - 1) {
|
||||
this.scrollPosition.emit('bottom');
|
||||
}
|
||||
// emit bottom event
|
||||
if (top > height - offset - 1) {
|
||||
this.scrollPosition.emit('bottom');
|
||||
}
|
||||
|
||||
// emit top event
|
||||
if (top === 0) {
|
||||
this.scrollPosition.emit('top');
|
||||
}
|
||||
// emit top event
|
||||
if (top === 0) {
|
||||
this.scrollPosition.emit('top');
|
||||
}
|
||||
|
||||
} catch (err) { }
|
||||
}
|
||||
} catch (err) { }
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,22 +1,22 @@
|
||||
<div class="card" appOutsideClick (clickOutside)="closeCard($event)">
|
||||
<app-avatar
|
||||
<div class="card" cnslOutsideClick (clickOutside)="closeCard($event)">
|
||||
<cnsl-avatar
|
||||
*ngIf="user.human?.profile && (user.human?.profile?.displayName || (user.human?.profile?.firstName && user.human?.profile?.lastName))"
|
||||
class="avatar" [forColor]="user.preferredLoginName" [avatarUrl]="user.human?.profile?.avatarUrl || ''"
|
||||
[name]="user.human?.profile?.displayName ? user.human?.profile?.displayName : (user.human?.profile?.firstName + ' '+ user.human?.profile?.lastName)"
|
||||
[name]="(user.human && user.human.profile && user.human.profile.displayName) ? user.human.profile.displayName : (user.human?.profile?.firstName + ' '+ user.human?.profile?.lastName)"
|
||||
[size]="80">
|
||||
</app-avatar>
|
||||
</cnsl-avatar>
|
||||
|
||||
<span class="u-name">{{user.human?.profile?.displayName ? user.human?.profile?.displayName : 'A'}}</span>
|
||||
<span class="u-email">{{user?.preferredLoginName}}</span>
|
||||
<span class="u-email" *ngIf="user.preferredLoginName">{{user.preferredLoginName}}</span>
|
||||
<span class="iamuser" *ngIf="iamuser">IAM USER</span>
|
||||
|
||||
<button color="primary" (click)="editUserProfile()" mat-stroked-button>{{'USER.EDITACCOUNT' | translate}}</button>
|
||||
<div class="l-accounts">
|
||||
<mat-progress-bar *ngIf="loadingUsers" color="primary" mode="indeterminate"></mat-progress-bar>
|
||||
<a class="row" *ngFor="let session of sessions" (click)="selectAccount(session.loginName)">
|
||||
<app-avatar *ngIf="session && session.displayName" class="small-avatar" [avatarUrl]="session.avatarUrl || ''"
|
||||
<cnsl-avatar *ngIf="session && session.displayName" class="small-avatar" [avatarUrl]="session.avatarUrl || ''"
|
||||
[forColor]="session.loginName" [size]="32">
|
||||
</app-avatar>
|
||||
</cnsl-avatar>
|
||||
|
||||
<div class="col">
|
||||
<span class="user-title">{{session.displayName ? session.displayName : session.userName}} </span>
|
||||
|
@ -6,15 +6,15 @@ import { AuthenticationService } from 'src/app/services/authentication.service';
|
||||
import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-accounts-card',
|
||||
selector: 'cnsl-accounts-card',
|
||||
templateUrl: './accounts-card.component.html',
|
||||
styleUrls: ['./accounts-card.component.scss'],
|
||||
})
|
||||
export class AccountsCardComponent implements OnInit {
|
||||
@Input() public user!: User.AsObject;
|
||||
@Input() public iamuser: boolean = false;
|
||||
@Input() public iamuser: boolean | null = false;
|
||||
|
||||
@Output() public close: EventEmitter<void> = new EventEmitter();
|
||||
@Output() public closedCard: EventEmitter<void> = new EventEmitter();
|
||||
public sessions: Session.AsObject[] = [];
|
||||
public loadingUsers: boolean = false;
|
||||
constructor(public authService: AuthenticationService, private router: Router, private userService: GrpcAuthService) {
|
||||
@ -37,12 +37,12 @@ export class AccountsCardComponent implements OnInit {
|
||||
|
||||
public editUserProfile(): void {
|
||||
this.router.navigate(['users/me']);
|
||||
this.close.emit();
|
||||
this.closedCard.emit();
|
||||
}
|
||||
|
||||
public closeCard(element: HTMLElement): void {
|
||||
if (!element.classList.contains('dontcloseonclick')) {
|
||||
this.close.emit();
|
||||
this.closedCard.emit();
|
||||
}
|
||||
}
|
||||
|
||||
@ -69,6 +69,6 @@ export class AccountsCardComponent implements OnInit {
|
||||
|
||||
public logout(): void {
|
||||
this.authService.signout();
|
||||
this.close.emit();
|
||||
this.closedCard.emit();
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,7 @@
|
||||
{{'ACTIONS.CANCEL' | translate}}
|
||||
</button>
|
||||
|
||||
<button color="primary" mat-raised-button class="ok-button" [disabled]="type == undefined || dateControl.invalid"
|
||||
<button color="primary" mat-raised-button class="ok-button" [disabled]="type === undefined || dateControl.invalid"
|
||||
(click)="closeDialogWithSuccess()">
|
||||
{{'ACTIONS.ADD' | translate}}
|
||||
</button>
|
||||
|
@ -4,36 +4,36 @@ import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { KeyType } from 'src/app/proto/generated/zitadel/auth_n_key_pb';
|
||||
|
||||
export enum AddKeyDialogType {
|
||||
MACHINE = 'MACHINE',
|
||||
AUTHNKEY = 'AUTHNKEY',
|
||||
MACHINE = 'MACHINE',
|
||||
AUTHNKEY = 'AUTHNKEY',
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-add-key-dialog',
|
||||
templateUrl: './add-key-dialog.component.html',
|
||||
styleUrls: ['./add-key-dialog.component.scss'],
|
||||
selector: 'cnsl-add-key-dialog',
|
||||
templateUrl: './add-key-dialog.component.html',
|
||||
styleUrls: ['./add-key-dialog.component.scss'],
|
||||
})
|
||||
export class AddKeyDialogComponent {
|
||||
public startDate: Date = new Date();
|
||||
types: KeyType[] = [];
|
||||
public type!: KeyType;
|
||||
public dateControl: FormControl = new FormControl('', []);
|
||||
public startDate: Date = new Date();
|
||||
types: KeyType[] = [];
|
||||
public type!: KeyType;
|
||||
public dateControl: FormControl = new FormControl('', []);
|
||||
|
||||
constructor(
|
||||
public dialogRef: MatDialogRef<AddKeyDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public data: any,
|
||||
) {
|
||||
this.types = [KeyType.KEY_TYPE_JSON];
|
||||
this.type = KeyType.KEY_TYPE_JSON;
|
||||
const today = new Date();
|
||||
this.startDate.setDate(today.getDate() + 1);
|
||||
}
|
||||
constructor(
|
||||
public dialogRef: MatDialogRef<AddKeyDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public data: any,
|
||||
) {
|
||||
this.types = [KeyType.KEY_TYPE_JSON];
|
||||
this.type = KeyType.KEY_TYPE_JSON;
|
||||
const today = new Date();
|
||||
this.startDate.setDate(today.getDate() + 1);
|
||||
}
|
||||
|
||||
public closeDialog(): void {
|
||||
this.dialogRef.close(false);
|
||||
}
|
||||
public closeDialog(): void {
|
||||
this.dialogRef.close(false);
|
||||
}
|
||||
|
||||
public closeDialogWithSuccess(): void {
|
||||
this.dialogRef.close({ type: this.type, date: this.dateControl.value });
|
||||
}
|
||||
public closeDialogWithSuccess(): void {
|
||||
this.dialogRef.close({ type: this.type, date: this.dateControl.value });
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,7 @@
|
||||
<cnsl-label>{{ 'MEMBER.CREATIONTYPE' | translate }}</cnsl-label>
|
||||
<mat-select [(ngModel)]="creationType" (selectionChange)="loadRoles()">
|
||||
<mat-option *ngFor="let type of creationTypes" [value]="type.type"
|
||||
[disabled]="(type.disabled$ | async) == false">
|
||||
[disabled]="(type.disabled$ | async) === false">
|
||||
{{ 'MEMBER.CREATIONTYPES.'+type.type | translate}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
@ -19,16 +19,16 @@
|
||||
<ng-container
|
||||
*ngIf="creationType === CreationType.PROJECT_OWNED || creationType === CreationType.PROJECT_GRANTED">
|
||||
<p>{{'PROJECT.GRANT.CREATE.SEL_PROJECT' | translate}}</p>
|
||||
<app-search-project-autocomplete class="block" singleOutput="true"
|
||||
<cnsl-search-project-autocomplete class="block" [singleOutput]="true"
|
||||
(selectionChanged)="selectProject($event)"
|
||||
[autocompleteType]="creationType === CreationType.PROJECT_OWNED ? ProjectAutocompleteType.PROJECT_OWNED : creationType === CreationType.PROJECT_GRANTED ? ProjectAutocompleteType.PROJECT_GRANTED : undefined">
|
||||
</app-search-project-autocomplete>
|
||||
</cnsl-search-project-autocomplete>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
<!-- if no context end -->
|
||||
|
||||
<app-search-user-autocomplete [users]="preselectedUsers" (selectionChanged)="users = $event">
|
||||
</app-search-user-autocomplete>
|
||||
<cnsl-search-user-autocomplete [users]="preselectedUsers" (selectionChanged)="users = $any($event)">
|
||||
</cnsl-search-user-autocomplete>
|
||||
|
||||
<cnsl-form-field class="full-width" appearance="outline"
|
||||
*ngIf="creationType === CreationType.PROJECT_OWNED || creationType === CreationType.PROJECT_GRANTED || creationType === CreationType.IAM">
|
||||
@ -41,8 +41,8 @@
|
||||
</cnsl-form-field>
|
||||
|
||||
<ng-container *ngIf="creationType === CreationType.ORG">
|
||||
<app-org-member-roles-autocomplete (selectionChanged)="setOrgMemberRoles($event)">
|
||||
</app-org-member-roles-autocomplete>
|
||||
<cnsl-org-member-roles-autocomplete (selectionChanged)="setOrgMemberRoles($event)">
|
||||
</cnsl-org-member-roles-autocomplete>
|
||||
</ng-container>
|
||||
|
||||
</div>
|
||||
@ -51,7 +51,7 @@
|
||||
{{'ACTIONS.CANCEL' | translate}}
|
||||
</button>
|
||||
|
||||
<button [disabled]="users.length == 0 || roles.length == 0" color="primary" mat-raised-button class="ok-button"
|
||||
<button [disabled]="users.length === 0 || roles.length === 0" color="primary" mat-raised-button class="ok-button"
|
||||
(click)="closeDialogWithSuccess()">
|
||||
{{'ACTIONS.ADD' | translate}}
|
||||
</button>
|
||||
|
@ -11,115 +11,115 @@ import { ToastService } from 'src/app/services/toast.service';
|
||||
import { ProjectAutocompleteType } from '../search-project-autocomplete/search-project-autocomplete.component';
|
||||
|
||||
export enum CreationType {
|
||||
PROJECT_OWNED = 0,
|
||||
PROJECT_GRANTED = 1,
|
||||
ORG = 2,
|
||||
IAM = 3,
|
||||
PROJECT_OWNED = 0,
|
||||
PROJECT_GRANTED = 1,
|
||||
ORG = 2,
|
||||
IAM = 3,
|
||||
}
|
||||
@Component({
|
||||
selector: 'app-member-create-dialog',
|
||||
templateUrl: './member-create-dialog.component.html',
|
||||
styleUrls: ['./member-create-dialog.component.scss'],
|
||||
selector: 'cnsl-member-create-dialog',
|
||||
templateUrl: './member-create-dialog.component.html',
|
||||
styleUrls: ['./member-create-dialog.component.scss'],
|
||||
})
|
||||
export class MemberCreateDialogComponent {
|
||||
private projectId: string = '';
|
||||
private grantId: string = '';
|
||||
public preselectedUsers: Array<User.AsObject> = [];
|
||||
private projectId: string = '';
|
||||
private grantId: string = '';
|
||||
public preselectedUsers: Array<User.AsObject> = [];
|
||||
|
||||
public creationType!: CreationType;
|
||||
public creationType!: CreationType;
|
||||
|
||||
/**
|
||||
* Specifies options for creating members,
|
||||
* without ending $, to enable write event permission even if user is allowed
|
||||
* to create members for only one specific project.
|
||||
*/
|
||||
public creationTypes: Array<{ type: CreationType, disabled$: Observable<boolean>; }> = [
|
||||
{ type: CreationType.IAM, disabled$: this.authService.isAllowed(['iam.member.write$']) },
|
||||
{ type: CreationType.ORG, disabled$: this.authService.isAllowed(['org.member.write$']) },
|
||||
{ type: CreationType.PROJECT_OWNED, disabled$: this.authService.isAllowed(['project.member.write']) },
|
||||
{ type: CreationType.PROJECT_GRANTED, disabled$: this.authService.isAllowed(['project.grant.member.write']) },
|
||||
];
|
||||
public users: Array<User.AsObject> = [];
|
||||
public roles: Array<Role.AsObject> | string[] = [];
|
||||
public CreationType: any = CreationType;
|
||||
public ProjectAutocompleteType: any = ProjectAutocompleteType;
|
||||
public memberRoleOptions: string[] = [];
|
||||
/**
|
||||
* Specifies options for creating members,
|
||||
* without ending $, to enable write event permission even if user is allowed
|
||||
* to create members for only one specific project.
|
||||
*/
|
||||
public creationTypes: Array<{ type: CreationType, disabled$: Observable<boolean>; }> = [
|
||||
{ type: CreationType.IAM, disabled$: this.authService.isAllowed(['iam.member.write$']) },
|
||||
{ type: CreationType.ORG, disabled$: this.authService.isAllowed(['org.member.write$']) },
|
||||
{ type: CreationType.PROJECT_OWNED, disabled$: this.authService.isAllowed(['project.member.write']) },
|
||||
{ type: CreationType.PROJECT_GRANTED, disabled$: this.authService.isAllowed(['project.grant.member.write']) },
|
||||
];
|
||||
public users: Array<User.AsObject> = [];
|
||||
public roles: Array<Role.AsObject> | string[] = [];
|
||||
public CreationType: any = CreationType;
|
||||
public ProjectAutocompleteType: any = ProjectAutocompleteType;
|
||||
public memberRoleOptions: string[] = [];
|
||||
|
||||
public showCreationTypeSelector: boolean = false;
|
||||
constructor(
|
||||
private mgmtService: ManagementService,
|
||||
private adminService: AdminService,
|
||||
private authService: GrpcAuthService,
|
||||
public dialogRef: MatDialogRef<MemberCreateDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public data: any,
|
||||
private toastService: ToastService,
|
||||
) {
|
||||
if (data?.projectId) {
|
||||
this.projectId = data.projectId;
|
||||
}
|
||||
if (data?.user) {
|
||||
this.preselectedUsers = [data.user];
|
||||
this.users = [data.user];
|
||||
}
|
||||
|
||||
if (data?.creationType !== undefined) {
|
||||
this.creationType = data.creationType;
|
||||
this.loadRoles();
|
||||
} else {
|
||||
this.showCreationTypeSelector = true;
|
||||
}
|
||||
public showCreationTypeSelector: boolean = false;
|
||||
constructor(
|
||||
private mgmtService: ManagementService,
|
||||
private adminService: AdminService,
|
||||
private authService: GrpcAuthService,
|
||||
public dialogRef: MatDialogRef<MemberCreateDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public data: any,
|
||||
private toastService: ToastService,
|
||||
) {
|
||||
if (data?.projectId) {
|
||||
this.projectId = data.projectId;
|
||||
}
|
||||
if (data?.user) {
|
||||
this.preselectedUsers = [data.user];
|
||||
this.users = [data.user];
|
||||
}
|
||||
|
||||
public loadRoles(): void {
|
||||
switch (this.creationType) {
|
||||
case CreationType.PROJECT_GRANTED:
|
||||
this.mgmtService.listProjectGrantMemberRoles().then(resp => {
|
||||
this.memberRoleOptions = resp.resultList;
|
||||
}).catch(error => {
|
||||
this.toastService.showError(error);
|
||||
});
|
||||
break;
|
||||
case CreationType.PROJECT_OWNED:
|
||||
this.mgmtService.listProjectMemberRoles().then(resp => {
|
||||
this.memberRoleOptions = resp.resultList;
|
||||
}).catch(error => {
|
||||
this.toastService.showError(error);
|
||||
});
|
||||
break;
|
||||
case CreationType.IAM:
|
||||
this.adminService.listIAMMemberRoles().then(resp => {
|
||||
this.memberRoleOptions = resp.rolesList;
|
||||
}).catch(error => {
|
||||
this.toastService.showError(error);
|
||||
});
|
||||
break;
|
||||
}
|
||||
if (data?.creationType !== undefined) {
|
||||
this.creationType = data.creationType;
|
||||
this.loadRoles();
|
||||
} else {
|
||||
this.showCreationTypeSelector = true;
|
||||
}
|
||||
}
|
||||
|
||||
public selectProject(project: Project.AsObject | GrantedProject.AsObject | any): void {
|
||||
if (project.projectId && project.grantId) {
|
||||
this.projectId = project.projectId;
|
||||
this.grantId = project.grantId;
|
||||
} else if (project.id) {
|
||||
this.projectId = project.id;
|
||||
}
|
||||
}
|
||||
|
||||
public closeDialog(): void {
|
||||
this.dialogRef.close(false);
|
||||
}
|
||||
|
||||
public closeDialogWithSuccess(): void {
|
||||
this.dialogRef.close({
|
||||
users: this.users,
|
||||
roles: this.roles,
|
||||
creationType: this.creationType,
|
||||
projectId: this.projectId,
|
||||
grantId: this.grantId,
|
||||
public loadRoles(): void {
|
||||
switch (this.creationType) {
|
||||
case CreationType.PROJECT_GRANTED:
|
||||
this.mgmtService.listProjectGrantMemberRoles().then(resp => {
|
||||
this.memberRoleOptions = resp.resultList;
|
||||
}).catch(error => {
|
||||
this.toastService.showError(error);
|
||||
});
|
||||
break;
|
||||
case CreationType.PROJECT_OWNED:
|
||||
this.mgmtService.listProjectMemberRoles().then(resp => {
|
||||
this.memberRoleOptions = resp.resultList;
|
||||
}).catch(error => {
|
||||
this.toastService.showError(error);
|
||||
});
|
||||
break;
|
||||
case CreationType.IAM:
|
||||
this.adminService.listIAMMemberRoles().then(resp => {
|
||||
this.memberRoleOptions = resp.rolesList;
|
||||
}).catch(error => {
|
||||
this.toastService.showError(error);
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public setOrgMemberRoles(roles: string[]): void {
|
||||
this.roles = roles;
|
||||
public selectProject(project: Project.AsObject | GrantedProject.AsObject | any): void {
|
||||
if (project.projectId && project.grantId) {
|
||||
this.projectId = project.projectId;
|
||||
this.grantId = project.grantId;
|
||||
} else if (project.id) {
|
||||
this.projectId = project.id;
|
||||
}
|
||||
}
|
||||
|
||||
public closeDialog(): void {
|
||||
this.dialogRef.close(false);
|
||||
}
|
||||
|
||||
public closeDialogWithSuccess(): void {
|
||||
this.dialogRef.close({
|
||||
users: this.users,
|
||||
roles: this.roles,
|
||||
creationType: this.creationType,
|
||||
projectId: this.projectId,
|
||||
grantId: this.grantId,
|
||||
});
|
||||
}
|
||||
|
||||
public setOrgMemberRoles(roles: string[]): void {
|
||||
this.roles = roles;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
<div class="cnsl-app-card" [ngClass]="{'web': type == OIDCAppType.OIDC_APP_TYPE_WEB,
|
||||
'useragent': type == OIDCAppType.OIDC_APP_TYPE_USER_AGENT,
|
||||
'native': type == OIDCAppType.OIDC_APP_TYPE_NATIVE, 'api': isApiApp}">
|
||||
<div class="cnsl-app-card" [ngClass]="{'web': type === OIDCAppType.OIDC_APP_TYPE_WEB,
|
||||
'useragent': type === OIDCAppType.OIDC_APP_TYPE_USER_AGENT,
|
||||
'native': type === OIDCAppType.OIDC_APP_TYPE_NATIVE, 'api': isApiApp}">
|
||||
<ng-content></ng-content>
|
||||
</div>
|
@ -2,13 +2,13 @@ import { Component, Input } from '@angular/core';
|
||||
import { OIDCAppType } from 'src/app/proto/generated/zitadel/app_pb';
|
||||
|
||||
@Component({
|
||||
selector: 'cnsl-app-card',
|
||||
templateUrl: './app-card.component.html',
|
||||
styleUrls: ['./app-card.component.scss'],
|
||||
selector: 'cnsl-app-card',
|
||||
templateUrl: './app-card.component.html',
|
||||
styleUrls: ['./app-card.component.scss'],
|
||||
})
|
||||
export class AppCardComponent {
|
||||
@Input() public outline: boolean = false;
|
||||
@Input() public type!: OIDCAppType;
|
||||
@Input() public isApiApp: boolean = false;
|
||||
public OIDCAppType: any = OIDCAppType;
|
||||
@Input() public outline: boolean = false;
|
||||
@Input() public type: OIDCAppType | undefined = undefined;
|
||||
@Input() public isApiApp: boolean = false;
|
||||
public OIDCAppType: any = OIDCAppType;
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
<ng-container *ngFor="let method of authMethods; index as i">
|
||||
<input type="radio" [disabled]="method.disabled" (change)="emitChange()" [value]="method.key" [id]="method.key"
|
||||
[(ngModel)]="selected" />
|
||||
<label class="cnsl-radio-button" [ngClass]="{'first': i == 0, 'last': i == authMethods.length - 1}"
|
||||
<label class="cnsl-radio-button" [ngClass]="{'first': i === 0, 'last': i === authMethods.length - 1}"
|
||||
[for]="method.key">
|
||||
<div class="recommended" [ngClass]="{'not': method.notRecommended}"
|
||||
*ngIf="method.recommended || method.notRecommended">
|
||||
@ -11,25 +11,25 @@
|
||||
|
||||
<div class="cnsl-radio-header" [ngStyle]="{'background': method.background}">
|
||||
<span>{{method.prefix}}</span>
|
||||
<div class="current" *ngIf="current == method.key">{{'APP.OIDC.CURRENT' | translate}}</div>
|
||||
<div class="current" *ngIf="current === method.key">{{'APP.OIDC.CURRENT' | translate}}</div>
|
||||
</div>
|
||||
<p>{{method.titleI18nKey | translate}}</p>
|
||||
<p class="type-desc">{{method.descI18nKey | translate}}</p>
|
||||
<span class="fill-space"></span>
|
||||
<div class="app-specs">
|
||||
<div class="row" *ngIf="isOIDC && method && method.responseType != undefined">
|
||||
<div class="row" *ngIf="isOIDC && method && method.responseType !== undefined">
|
||||
<span>{{'APP.OIDC.RESPONSETYPE' | translate}}</span>
|
||||
<span>{{('APP.OIDC.RESPONSE.'+method.responseType.toString()) | translate}}</span>
|
||||
</div>
|
||||
<div class="row" *ngIf="isOIDC && method.grantType != undefined">
|
||||
<div class="row" *ngIf="isOIDC && method.grantType !== undefined">
|
||||
<span>{{'APP.GRANT' | translate}}</span>
|
||||
<span>{{('APP.OIDC.GRANT.'+method.grantType.toString()) | translate}}</span>
|
||||
</div>
|
||||
<div class="row" *ngIf="isOIDC && method.authMethod != undefined">
|
||||
<div class="row" *ngIf="isOIDC && method.authMethod !== undefined">
|
||||
<span>{{'APP.AUTHMETHOD' | translate}}</span>
|
||||
<span>{{('APP.OIDC.AUTHMETHOD.'+method.authMethod.toString()) | translate}}</span>
|
||||
</div>
|
||||
<div class="row" *ngIf="!isOIDC && method.apiAuthMethod != undefined">
|
||||
<div class="row" *ngIf="!isOIDC && method.apiAuthMethod !== undefined">
|
||||
<span>{{'APP.AUTHMETHOD' | translate}}</span>
|
||||
<span>{{('APP.API.AUTHMETHOD.'+method.apiAuthMethod.toString()) | translate}}</span>
|
||||
</div>
|
||||
|
@ -1,39 +1,39 @@
|
||||
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
import {
|
||||
APIAuthMethodType,
|
||||
OIDCAuthMethodType,
|
||||
OIDCGrantType,
|
||||
OIDCResponseType,
|
||||
APIAuthMethodType,
|
||||
OIDCAuthMethodType,
|
||||
OIDCGrantType,
|
||||
OIDCResponseType,
|
||||
} from 'src/app/proto/generated/zitadel/app_pb';
|
||||
|
||||
export interface RadioItemAuthType {
|
||||
key: string;
|
||||
titleI18nKey: string;
|
||||
descI18nKey: string;
|
||||
disabled: boolean;
|
||||
prefix: string;
|
||||
background: string;
|
||||
responseType?: OIDCResponseType;
|
||||
grantType?: OIDCGrantType;
|
||||
authMethod?: OIDCAuthMethodType;
|
||||
apiAuthMethod?: | APIAuthMethodType;
|
||||
recommended?: boolean;
|
||||
notRecommended?: boolean;
|
||||
key: string;
|
||||
titleI18nKey: string;
|
||||
descI18nKey: string;
|
||||
disabled: boolean;
|
||||
prefix: string;
|
||||
background: string;
|
||||
responseType?: OIDCResponseType;
|
||||
grantType?: OIDCGrantType;
|
||||
authMethod?: OIDCAuthMethodType;
|
||||
apiAuthMethod?: | APIAuthMethodType;
|
||||
recommended?: boolean;
|
||||
notRecommended?: boolean;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-auth-method-radio',
|
||||
templateUrl: './app-auth-method-radio.component.html',
|
||||
styleUrls: ['./app-auth-method-radio.component.scss'],
|
||||
selector: 'cnsl-auth-method-radio',
|
||||
templateUrl: './app-auth-method-radio.component.html',
|
||||
styleUrls: ['./app-auth-method-radio.component.scss'],
|
||||
})
|
||||
export class AppAuthMethodRadioComponent {
|
||||
@Input() current: string = '';
|
||||
@Input() selected: string = '';
|
||||
@Input() authMethods!: RadioItemAuthType[];
|
||||
@Input() isOIDC: boolean = false;
|
||||
@Output() selectedMethod: EventEmitter<string> = new EventEmitter();
|
||||
@Input() current: string = '';
|
||||
@Input() selected: string = '';
|
||||
@Input() authMethods!: RadioItemAuthType[];
|
||||
@Input() isOIDC: boolean = false;
|
||||
@Output() selectedMethod: EventEmitter<string> = new EventEmitter();
|
||||
|
||||
public emitChange(): void {
|
||||
this.selectedMethod.emit(this.selected);
|
||||
}
|
||||
public emitChange(): void {
|
||||
this.selectedMethod.emit(this.selected);
|
||||
}
|
||||
}
|
||||
|
@ -2,16 +2,16 @@ import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
import { RadioItemAppType, WEB_TYPE } from 'src/app/pages/projects/apps/authtypes';
|
||||
|
||||
@Component({
|
||||
selector: 'app-type-radio',
|
||||
templateUrl: './app-type-radio.component.html',
|
||||
styleUrls: ['./app-type-radio.component.scss'],
|
||||
selector: 'cnsl-type-radio',
|
||||
templateUrl: './app-type-radio.component.html',
|
||||
styleUrls: ['./app-type-radio.component.scss'],
|
||||
})
|
||||
export class AppTypeRadioComponent {
|
||||
@Input() selected: RadioItemAppType = WEB_TYPE;
|
||||
@Input() types!: RadioItemAppType[];
|
||||
@Output() selectedType: EventEmitter<RadioItemAppType> = new EventEmitter();
|
||||
@Input() selected: RadioItemAppType = WEB_TYPE;
|
||||
@Input() types!: RadioItemAppType[];
|
||||
@Output() selectedType: EventEmitter<RadioItemAppType> = new EventEmitter();
|
||||
|
||||
public emitChange(): void {
|
||||
this.selectedType.emit(this.selected);
|
||||
}
|
||||
public emitChange(): void {
|
||||
this.selectedType.emit(this.selected);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
<div class="avatar-circle dontcloseonclick" matRipple [matRippleColor]="'#ffffff20'" matRippleUnbounded="true"
|
||||
matRippleCentered="true"
|
||||
<div class="avatar-circle dontcloseonclick" matRipple [matRippleColor]="'#ffffff20'" [matRippleUnbounded]="true"
|
||||
[matRippleCentered]="true"
|
||||
[ngStyle]="{'height': size+'px', 'width': size+'px', 'fontSize': (fontSize-1)+'px', 'background': color}"
|
||||
[ngClass]="{'active': active}">
|
||||
<img class="dontcloseonclick" *ngIf="avatarUrl; else creds" [src]="avatarUrl"/>
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-avatar',
|
||||
selector: 'cnsl-avatar',
|
||||
templateUrl: './avatar.component.html',
|
||||
styleUrls: ['./avatar.component.scss'],
|
||||
})
|
||||
@ -77,7 +77,7 @@ export class AvatarComponent implements OnInit {
|
||||
return colors[hash % colors.length];
|
||||
}
|
||||
|
||||
// tslint:disable
|
||||
/* eslint-disable */
|
||||
private hashCode(str: string, seed: number = 0): number {
|
||||
let h1 = 0xdeadbeef ^ seed, h2 = 0x41c6ce57 ^ seed;
|
||||
for (let i = 0, ch; i < str.length; i++) {
|
||||
@ -89,5 +89,5 @@ export class AvatarComponent implements OnInit {
|
||||
h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507) ^ Math.imul(h1 ^ (h1 >>> 13), 3266489909);
|
||||
return 4294967296 * (2097151 & h2) + (h1 >>> 0);
|
||||
}
|
||||
// tslint:enable
|
||||
/* eslint-enable */
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ import { animate, style, transition, trigger } from '@angular/animations';
|
||||
import { Component, Input } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-card',
|
||||
selector: 'cnsl-card',
|
||||
templateUrl: './card.component.html',
|
||||
styleUrls: ['./card.component.scss'],
|
||||
animations: [
|
||||
|
@ -5,17 +5,18 @@
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="scroll-container" appScrollable (scrollPosition)="scrollHandler($event)">
|
||||
<div class="scroll-container" cnslScrollable (scrollPosition)="scrollHandler($event)">
|
||||
<li class="item change-item-back" *ngFor="let hist of data | async; index as histindex">
|
||||
<span *ngIf="hist.values[0].dates[0]" class="date">{{
|
||||
hist.values[0]?.dates[0]| timestampToDate | localizedDate: 'dd. MMMM YYYY' }}</span>
|
||||
<span *ngIf="hist.values[0].dates[0]" class="date">
|
||||
{{ hist.values[0].dates[0]| timestampToDate | localizedDate: 'dd. MMMM YYYY' }}
|
||||
</span>
|
||||
<div class="item" *ngFor="let dayelement of hist.values; index as i">
|
||||
<div class="row">
|
||||
<app-avatar matTooltip="{{ dayelement.editorDisplayName }}"
|
||||
<cnsl-avatar matTooltip="{{ dayelement.editorDisplayName }}"
|
||||
*ngIf="dayelement.editorDisplayName; else spacer" class="avatar"
|
||||
[name]="dayelement.editorDisplayName" [size]="32" [forColor]="dayelement?.editorPreferredLoginName"
|
||||
[name]="dayelement.editorDisplayName" [size]="32" [forColor]="dayelement?.editorPreferredLoginName ?? 'A'"
|
||||
[avatarUrl]="dayelement.editorAvatarUrl || ''">
|
||||
</app-avatar>
|
||||
</cnsl-avatar>
|
||||
<ng-template #spacer>
|
||||
<div class="spacer"></div>
|
||||
</ng-template>
|
||||
|
@ -29,6 +29,9 @@ export interface MappedChange {
|
||||
dates: Timestamp.AsObject[];
|
||||
editorId: string;
|
||||
editorName: string;
|
||||
editorDisplayName: string;
|
||||
editorAvatarUrl: string;
|
||||
editorPreferredLoginName: string;
|
||||
eventTypes: Array<{ key: string; localizedMessage: string; }>;
|
||||
sequences: number[];
|
||||
}>;
|
||||
@ -41,7 +44,7 @@ type ListChanges = ListMyUserChangesResponse.AsObject |
|
||||
ListAppChangesResponse.AsObject;
|
||||
|
||||
@Component({
|
||||
selector: 'app-changes',
|
||||
selector: 'cnsl-changes',
|
||||
templateUrl: './changes.component.html',
|
||||
styleUrls: ['./changes.component.scss'],
|
||||
})
|
||||
@ -253,7 +256,7 @@ export class ChangesComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
// Order by ascending property value
|
||||
// tslint:disable
|
||||
/* eslint-disable */
|
||||
valueAscOrder = (a: KeyValue<number, string>, b: KeyValue<number, string>): number => {
|
||||
return a.value.localeCompare(b.value);
|
||||
};
|
||||
@ -262,5 +265,5 @@ export class ChangesComponent implements OnInit, OnDestroy {
|
||||
keyDescOrder = (a: KeyValue<number, string>, b: KeyValue<number, string>): number => {
|
||||
return a.key > b.key ? -1 : (b.key > a.key ? 1 : 0);
|
||||
};
|
||||
// tslint:enable
|
||||
/* eslint-enable */
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
<app-refresh-table [loading]="loading$ | async" (refreshed)="refreshPage()" [dataSize]="dataSource.data.length"
|
||||
<cnsl-refresh-table [loading]="loading$ | async" (refreshed)="refreshPage()" [dataSize]="dataSource.data.length"
|
||||
[timestamp]="keyResult?.details?.viewTimestamp" [selection]="selection">
|
||||
<div actions>
|
||||
<a [disabled]="([('project.app.write:' + projectId), 'project.app.write'] | hasRole | async) == false"
|
||||
<a [disabled]="([('project.app.write:' + projectId), 'project.app.write'] | hasRole | async) === false"
|
||||
color="primary" mat-raised-button (click)="openAddKey()">
|
||||
<mat-icon class="icon">add</mat-icon>{{ 'ACTIONS.NEW' | translate }}
|
||||
</a>
|
||||
@ -51,7 +51,7 @@
|
||||
<th mat-header-cell *matHeaderCellDef></th>
|
||||
<td mat-cell *matCellDef="let key">
|
||||
<button
|
||||
[disabled]="([('project.app.write:' + projectId), 'project.app.write'] | hasRole | async) == false"
|
||||
[disabled]="([('project.app.write:' + projectId), 'project.app.write'] | hasRole | async) === false"
|
||||
mat-icon-button color="warn" matTooltip="{{'ACTIONS.DELETE' | translate}}"
|
||||
(click)="deleteKey(key)">
|
||||
<i class="las la-trash"></i>
|
||||
@ -68,4 +68,4 @@
|
||||
<cnsl-paginator #paginator class="paginator" [timestamp]="keyResult?.details?.viewTimestamp" [length]="keyResult?.details?.totalResult || 0" [pageSize]="10"
|
||||
[pageSizeOptions]="[5, 10, 20]" (page)="changePage($event)"></cnsl-paginator>
|
||||
</div>
|
||||
</app-refresh-table>
|
||||
</cnsl-refresh-table>
|
@ -16,7 +16,7 @@ import { ToastService } from 'src/app/services/toast.service';
|
||||
import { PageEvent, PaginatorComponent } from '../paginator/paginator.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-client-keys',
|
||||
selector: 'cnsl-client-keys',
|
||||
templateUrl: './client-keys.component.html',
|
||||
styleUrls: ['./client-keys.component.scss'],
|
||||
})
|
||||
@ -79,7 +79,7 @@ export class ClientKeysComponent implements OnInit {
|
||||
width: '400px',
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe(resp => {
|
||||
dialogRef.afterClosed().subscribe((resp) => {
|
||||
if (resp) {
|
||||
const type: KeyType = resp.type;
|
||||
|
||||
@ -96,7 +96,7 @@ export class ClientKeysComponent implements OnInit {
|
||||
}
|
||||
|
||||
if (type) {
|
||||
return this.mgmtService.addAppKey(
|
||||
this.mgmtService.addAppKey(
|
||||
this.projectId,
|
||||
this.appId,
|
||||
type,
|
||||
|
@ -6,16 +6,15 @@
|
||||
<div class="people">
|
||||
<div class="img-list" [@cardAnimation]="totalResult">
|
||||
<mat-spinner class="spinner" diameter="20" *ngIf="loading"></mat-spinner>
|
||||
|
||||
<ng-container *ngIf="totalResult < 10; else compact">
|
||||
<ng-container *ngFor="let member of membersSubject | async; index as i">
|
||||
<div @animate (click)="emitShowDetail()" class="avatar-circle"
|
||||
matTooltip="{{ member.displayName }} | {{member.rolesList?.join(' ')}}" [ngStyle]="{'z-index': 100 - i}">
|
||||
<app-avatar *ngIf="member && member.displayName && member.firstName && member.lastName; else cog"
|
||||
class="avatar dontcloseonclick" [avatarUrl]="member.avatarUrl|| ''" [forColor]="member?.userName"
|
||||
[forColor]="member?.preferredLoginName"
|
||||
<cnsl-avatar *ngIf="member && member.displayName && member.firstName && member.lastName; else cog"
|
||||
class="avatar dontcloseonclick" [avatarUrl]="member.avatarUrl|| ''"
|
||||
[forColor]="member.preferredLoginName ?? 'A'"
|
||||
[name]="member.displayName ? member.displayName : (member.firstName + ' '+ member.lastName)" [size]="32">
|
||||
</app-avatar>
|
||||
</cnsl-avatar>
|
||||
<ng-template #cog>
|
||||
<div class="sa-icon">
|
||||
<i class="las la-user-cog"></i>
|
||||
|
@ -4,46 +4,46 @@ import { BehaviorSubject } from 'rxjs';
|
||||
import { Member } from 'src/app/proto/generated/zitadel/member_pb';
|
||||
|
||||
@Component({
|
||||
selector: 'app-contributors',
|
||||
templateUrl: './contributors.component.html',
|
||||
styleUrls: ['./contributors.component.scss'],
|
||||
animations: [
|
||||
trigger('cardAnimation', [
|
||||
transition('* => *', [
|
||||
query('@animate', stagger('40ms', animateChild()), { optional: true }),
|
||||
]),
|
||||
]),
|
||||
trigger('animate', [
|
||||
transition(':enter', [
|
||||
animate('.2s ease-in', keyframes([
|
||||
style({ opacity: 0, offset: 0 }),
|
||||
style({ opacity: .5, transform: 'scale(1.05)', offset: 0.3 }),
|
||||
style({ opacity: 1, transform: 'scale(1)', offset: 1 }),
|
||||
])),
|
||||
]),
|
||||
]),
|
||||
],
|
||||
selector: 'cnsl-contributors',
|
||||
templateUrl: './contributors.component.html',
|
||||
styleUrls: ['./contributors.component.scss'],
|
||||
animations: [
|
||||
trigger('cardAnimation', [
|
||||
transition('* => *', [
|
||||
query('@animate', stagger('40ms', animateChild()), { optional: true }),
|
||||
]),
|
||||
]),
|
||||
trigger('animate', [
|
||||
transition(':enter', [
|
||||
animate('.2s ease-in', keyframes([
|
||||
style({ opacity: 0, offset: 0 }),
|
||||
style({ opacity: .5, transform: 'scale(1.05)', offset: 0.3 }),
|
||||
style({ opacity: 1, transform: 'scale(1)', offset: 1 }),
|
||||
])),
|
||||
]),
|
||||
]),
|
||||
],
|
||||
})
|
||||
export class ContributorsComponent {
|
||||
@Input() title: string = '';
|
||||
@Input() description: string = '';
|
||||
@Input() disabled: boolean = false;
|
||||
@Input() totalResult: number = 0;
|
||||
@Input() loading: boolean = false;
|
||||
@Input() membersSubject!: BehaviorSubject<Member.AsObject[]>;
|
||||
@Output() addClicked: EventEmitter<void> = new EventEmitter();
|
||||
@Output() showDetailClicked: EventEmitter<void> = new EventEmitter();
|
||||
@Output() refreshClicked: EventEmitter<void> = new EventEmitter();
|
||||
@Input() title: string = '';
|
||||
@Input() description: string = '';
|
||||
@Input() disabled: boolean = false;
|
||||
@Input() totalResult: number = 0;
|
||||
@Input() loading: boolean | null = false;
|
||||
@Input() membersSubject!: BehaviorSubject<Member.AsObject[]>;
|
||||
@Output() addClicked: EventEmitter<void> = new EventEmitter();
|
||||
@Output() showDetailClicked: EventEmitter<void> = new EventEmitter();
|
||||
@Output() refreshClicked: EventEmitter<void> = new EventEmitter();
|
||||
|
||||
public emitAddMember(): void {
|
||||
this.addClicked.emit();
|
||||
}
|
||||
public emitAddMember(): void {
|
||||
this.addClicked.emit();
|
||||
}
|
||||
|
||||
public emitShowDetail(): void {
|
||||
this.showDetailClicked.emit();
|
||||
}
|
||||
public emitShowDetail(): void {
|
||||
this.showDetailClicked.emit();
|
||||
}
|
||||
|
||||
public emitRefresh(): void {
|
||||
this.refreshClicked.emit();
|
||||
}
|
||||
public emitRefresh(): void {
|
||||
this.refreshClicked.emit();
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,12 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { RouterLink } from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'app-detail-layout',
|
||||
selector: 'cnsl-detail-layout',
|
||||
templateUrl: './detail-layout.component.html',
|
||||
styleUrls: ['./detail-layout.component.scss'],
|
||||
})
|
||||
export class DetailLayoutComponent {
|
||||
@Input() backRouterLink!: RouterLink;
|
||||
@Input() backRouterLink: any = undefined;
|
||||
@Input() title: string | null = '';
|
||||
@Input() description: string | null = '';
|
||||
@Input() maxWidth: boolean = true;
|
||||
|
@ -1,4 +1,4 @@
|
||||
<div *ngIf="currentMap">
|
||||
<div *ngIf="currentMap">
|
||||
|
||||
|
||||
<form [formGroup]="form" >
|
||||
@ -8,24 +8,24 @@
|
||||
<cnsl-form-field class="formfield" >
|
||||
<cnsl-label>{{key.key}}</cnsl-label>
|
||||
<textarea class="text" cnslInput [formControlName]="key.key" [placeholder]="defaultmap[key.key]" [name]="key.key" [ngClass]="{'defaulttext': form.get(key.key)?.value === ''}"></textarea>
|
||||
<div class="chips" *ngIf="warnText[key.key] == undefined">
|
||||
<div class="chips" *ngIf="warnText[key.key] === undefined">
|
||||
<ng-container *ngFor="let chip of chips" >
|
||||
<div class="chip" appCopyToClipboard [valueToCopy]="chip.value" (copiedValue)="copied = $event" (click)="addChip(key.key, chip.value)">
|
||||
<div class="chip" cnslCopyToClipboard [valueToCopy]="chip.value" (copiedValue)="copied = $event" (click)="addChip(key.key, chip.value)">
|
||||
<span class="key">{{chip.key | translate}}</span>
|
||||
<span class="value">{{chip.value}}</span>
|
||||
<i *ngIf="copied != chip.value" class="las la-clipboard"></i>
|
||||
<i *ngIf="copied == chip.value" class="las la-clipboard-check"></i>
|
||||
<i *ngIf="copied !== chip.value" class="las la-clipboard"></i>
|
||||
<i *ngIf="copied === chip.value" class="las la-clipboard-check"></i>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
</cnsl-form-field>
|
||||
<div class="actions">
|
||||
<button matTooltip="{{'ACTIONS.RESETDEFAULT'| translate }}" mat-icon-button [disabled]="form.get(key.key)?.value == defaultmap[key.key] || disabled" (click)="form.get(key.key)?.setValue(defaultmap[key.key])" (mouseenter) = "form.get(key.key)?.value != defaultmap[key.key] && setWarnText(key.key, defaultmap[key.key])" (mouseleave) ="setWarnText(key.key, undefined)"><i class="las la-history"></i></button>
|
||||
<button matTooltip="{{'ACTIONS.RESETCURRENT'| translate }}" mat-icon-button [disabled]="form.get(key.key)?.value == currentMap[key.key] || disabled" (click)="form.get(key.key)?.setValue(currentMap[key.key])" (mouseenter) = "form.get(key.key)?.value != currentMap[key.key] && setWarnText(key.key, currentMap[key.key])" (mouseleave) ="setWarnText(key.key, undefined)"><i class="las la-undo"></i></button>
|
||||
<button matTooltip="{{'ACTIONS.RESETDEFAULT'| translate }}" mat-icon-button [disabled]="form.get(key.key)?.value === defaultmap[key.key] || disabled" (click)="form.get(key.key)?.setValue(defaultmap[key.key])" (mouseenter) = "form.get(key.key)?.value !== defaultmap[key.key] && setWarnText(key.key, defaultmap[key.key])" (mouseleave) ="setWarnText(key.key, undefined)"><i class="las la-history"></i></button>
|
||||
<button matTooltip="{{'ACTIONS.RESETCURRENT'| translate }}" mat-icon-button [disabled]="form.get(key.key)?.value === currentMap[key.key] || disabled" (click)="form.get(key.key)?.setValue(currentMap[key.key])" (mouseenter) = "form.get(key.key)?.value !== currentMap[key.key] && setWarnText(key.key, currentMap[key.key])" (mouseleave) ="setWarnText(key.key, undefined)"><i class="las la-undo"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<cnsl-info-section *ngIf="warnText[key.key] !== undefined" class="info" type="WARN">{{'ACTIONS.RESETTO'| translate }} <cite>'{{warnText[key.key]}}'</cite></cnsl-info-section>
|
||||
<cnsl-info-section *ngIf="warnText[key.key] !== undefined" class="info" [type]="InfoSectionType.WARN">{{'ACTIONS.RESETTO'| translate }} <cite>'{{warnText[key.key]}}'</cite></cnsl-info-section>
|
||||
</ng-container>
|
||||
</form>
|
||||
</div>
|
||||
|
@ -3,6 +3,8 @@ import { FormControl, FormGroup } from '@angular/forms';
|
||||
import { Observable, Subject } from 'rxjs';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
|
||||
import { InfoSectionType } from '../info-section/info-section.component';
|
||||
|
||||
@Component({
|
||||
selector: 'cnsl-edit-text',
|
||||
templateUrl: './edit-text.component.html',
|
||||
@ -23,6 +25,7 @@ export class EditTextComponent implements OnInit, OnDestroy {
|
||||
@Input() public disabled: boolean = true;
|
||||
|
||||
public copied: string = '';
|
||||
public InfoSectionType: any = InfoSectionType;
|
||||
|
||||
public ngOnInit(): void {
|
||||
this.current$.pipe(takeUntil(this.destroy$)).subscribe(value => {
|
||||
|
@ -1,4 +1,4 @@
|
||||
<app-detail-layout [backRouterLink]="[ serviceType === FeatureServiceType.ADMIN ? '/iam/policies' : '/org']"
|
||||
<cnsl-detail-layout [backRouterLink]="[ serviceType === FeatureServiceType.ADMIN ? '/iam/policies' : '/org']"
|
||||
[title]="('FEATURES.TITLE' | translate)" [description]="'FEATURES.DESCRIPTION' | translate">
|
||||
|
||||
<h2>{{'FEATURES.TIER.TITLE' | translate}}</h2>
|
||||
@ -17,7 +17,7 @@
|
||||
|
||||
<ng-container *ngIf="serviceType === FeatureServiceType.MGMT">
|
||||
<mat-spinner class="spinner" diameter="20" *ngIf="customerLoading || stripeLoading"></mat-spinner>
|
||||
<div class="detail" *ngIf="stripeCustomer || stripeCustomer == null">
|
||||
<div class="detail" *ngIf="stripeCustomer || stripeCustomer === null">
|
||||
<p class="title">{{'FEATURES.TIER.DETAILS' | translate}}
|
||||
<a (click)="setCustomer()">{{'ACTIONS.EDIT' | translate}}</a>
|
||||
</p>
|
||||
@ -32,7 +32,7 @@
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<p class="error" *ngIf="(stripeCustomer || stripeCustomer == null) && !customerValid">{{'FEATURES.TIER.CUSTOMERINVALID' | translate}}</p>
|
||||
<p class="error" *ngIf="(stripeCustomer || stripeCustomer === null) && !customerValid">{{'FEATURES.TIER.CUSTOMERINVALID' | translate}}</p>
|
||||
|
||||
<div class="current-tier">
|
||||
<a color="primary" [disabled]="!org.id || !customerValid || !stripeURL" mat-raised-button [href]="stripeURL" target="_blank"
|
||||
@ -40,7 +40,7 @@
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ng-template appHasRole [appHasRole]="['iam.features.delete']">
|
||||
<ng-template cnslHasRole [hasRole]="['iam.features.delete']">
|
||||
<button *ngIf="serviceType === FeatureServiceType.MGMT && !isDefault"
|
||||
matTooltip="{{'POLICY.RESET' | translate}}" color="warn" (click)="resetFeatures()" mat-stroked-button>
|
||||
{{'POLICY.RESET' | translate}}
|
||||
@ -268,12 +268,12 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="btn-container" *ngIf="(['iam.features.write'] | hasRole | async) == true">
|
||||
<div class="btn-container" *ngIf="(['iam.features.write'] | hasRole | async) === true">
|
||||
<button (click)="savePolicy()" color="primary"
|
||||
type="submit" mat-raised-button>{{ 'ACTIONS.SAVE' | translate
|
||||
}}</button>
|
||||
</div>
|
||||
</app-detail-layout>
|
||||
</cnsl-detail-layout>
|
||||
|
||||
<ng-template #templateRef let-active="active">
|
||||
<span class="state" [ngClass]="{'active': active, 'inactive': !active}">
|
||||
|
@ -26,7 +26,7 @@ export enum FeatureServiceType {
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-features',
|
||||
selector: 'cnsl-features',
|
||||
templateUrl: './features.component.html',
|
||||
styleUrls: ['./features.component.scss'],
|
||||
})
|
||||
@ -133,15 +133,16 @@ export class FeaturesComponent implements OnDestroy {
|
||||
});
|
||||
}
|
||||
|
||||
private async getData(): Promise<GetFeaturesResponse.AsObject | GetOrgFeaturesResponse.AsObject | undefined> {
|
||||
private async getData(): Promise<GetFeaturesResponse.AsObject | GetOrgFeaturesResponse.AsObject> {
|
||||
switch (this.serviceType) {
|
||||
case FeatureServiceType.MGMT:
|
||||
return this.managementService.getFeatures();
|
||||
case FeatureServiceType.ADMIN:
|
||||
if (this.org?.id) {
|
||||
return this.adminService.getDefaultFeatures();
|
||||
} else {
|
||||
return Promise.reject();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,7 @@ function compare(a: Country, b: Country): number {
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-payment-info-dialog',
|
||||
selector: 'cnsl-payment-info-dialog',
|
||||
templateUrl: './payment-info-dialog.component.html',
|
||||
styleUrls: ['./payment-info-dialog.component.scss'],
|
||||
})
|
||||
|
@ -5,16 +5,16 @@ let nextUniqueId = 0;
|
||||
export const CNSL_ERROR = new InjectionToken<CnslErrorDirective>('CnslError');
|
||||
|
||||
@Directive({
|
||||
selector: '[cnsl-error]',
|
||||
host: {
|
||||
'class': 'cnsl-error',
|
||||
'role': 'alert',
|
||||
'[attr.id]': 'id',
|
||||
},
|
||||
providers: [{ provide: CNSL_ERROR, useExisting: CnslErrorDirective }],
|
||||
selector: '[cnslError]',
|
||||
host: {
|
||||
'class': 'cnsl-error',
|
||||
'role': 'alert',
|
||||
'[attr.id]': 'id',
|
||||
},
|
||||
providers: [{ provide: CNSL_ERROR, useExisting: CnslErrorDirective }],
|
||||
})
|
||||
export class CnslErrorDirective {
|
||||
@Input() id: string = `cnsl-error-${nextUniqueId++}`;
|
||||
@Input() id: string = `cnsl-error-${nextUniqueId++}`;
|
||||
|
||||
constructor() { }
|
||||
constructor() { }
|
||||
}
|
||||
|
@ -1,19 +1,19 @@
|
||||
import {
|
||||
AfterContentInit,
|
||||
AfterViewInit,
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
ContentChild,
|
||||
ContentChildren,
|
||||
ElementRef,
|
||||
HostListener,
|
||||
Inject,
|
||||
InjectionToken,
|
||||
OnDestroy,
|
||||
QueryList,
|
||||
ViewChild,
|
||||
ViewEncapsulation,
|
||||
AfterContentInit,
|
||||
AfterViewInit,
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
ContentChild,
|
||||
ContentChildren,
|
||||
ElementRef,
|
||||
HostListener,
|
||||
Inject,
|
||||
InjectionToken,
|
||||
OnDestroy,
|
||||
QueryList,
|
||||
ViewChild,
|
||||
ViewEncapsulation,
|
||||
} from '@angular/core';
|
||||
import { NgControl } from '@angular/forms';
|
||||
import { MatFormFieldControl } from '@angular/material/form-field';
|
||||
@ -22,142 +22,140 @@ import { startWith, takeUntil } from 'rxjs/operators';
|
||||
|
||||
import { cnslFormFieldAnimations } from './animations';
|
||||
import { CNSL_ERROR, CnslErrorDirective } from './error.directive';
|
||||
import { _CNSL_HINT, CnslHintDirective } from './hint.directive';
|
||||
|
||||
export const CNSL_FORM_FIELD = new InjectionToken<CnslFormFieldComponent>('CnslFormFieldComponent');
|
||||
|
||||
class CnslFormFieldBase {
|
||||
constructor(public _elementRef: ElementRef) { }
|
||||
constructor(public _elementRef: ElementRef) { }
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'cnsl-form-field',
|
||||
templateUrl: './form-field.component.html',
|
||||
styleUrls: ['./form-field.component.scss'],
|
||||
providers: [
|
||||
{ provide: CNSL_FORM_FIELD, useExisting: CnslFormFieldComponent },
|
||||
],
|
||||
host: {
|
||||
'[class.ng-untouched]': '_shouldForward("untouched")',
|
||||
'[class.ng-touched]': '_shouldForward("touched")',
|
||||
'[class.ng-pristine]': '_shouldForward("pristine")',
|
||||
'[class.ng-dirty]': '_shouldForward("dirty")',
|
||||
'[class.ng-valid]': '_shouldForward("valid")',
|
||||
'[class.ng-invalid]': '_shouldForward("invalid")',
|
||||
'[class.ng-pending]': '_shouldForward("pending")',
|
||||
'[class.cnsl-form-field-disabled]': '_control.disabled',
|
||||
'[class.cnsl-form-field-autofilled]': '_control.autofilled',
|
||||
'[class.cnsl-focused]': '_control.focused',
|
||||
'[class.cnsl-form-field-invalid]': '_control.errorState',
|
||||
},
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
animations: [cnslFormFieldAnimations.transitionMessages],
|
||||
selector: 'cnsl-form-field',
|
||||
templateUrl: './form-field.component.html',
|
||||
styleUrls: ['./form-field.component.scss'],
|
||||
providers: [
|
||||
{ provide: CNSL_FORM_FIELD, useExisting: CnslFormFieldComponent },
|
||||
],
|
||||
host: {
|
||||
'[class.ng-untouched]': '_shouldForward("untouched")',
|
||||
'[class.ng-touched]': '_shouldForward("touched")',
|
||||
'[class.ng-pristine]': '_shouldForward("pristine")',
|
||||
'[class.ng-dirty]': '_shouldForward("dirty")',
|
||||
'[class.ng-valid]': '_shouldForward("valid")',
|
||||
'[class.ng-invalid]': '_shouldForward("invalid")',
|
||||
'[class.ng-pending]': '_shouldForward("pending")',
|
||||
'[class.cnsl-form-field-disabled]': '_control.disabled',
|
||||
'[class.cnsl-form-field-autofilled]': '_control.autofilled',
|
||||
'[class.cnsl-focused]': '_control.focused',
|
||||
'[class.cnsl-form-field-invalid]': '_control.errorState',
|
||||
},
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
animations: [cnslFormFieldAnimations.transitionMessages],
|
||||
})
|
||||
export class CnslFormFieldComponent extends CnslFormFieldBase implements OnDestroy, AfterContentInit, AfterViewInit {
|
||||
focused: boolean = false;
|
||||
private _destroyed: Subject<void> = new Subject<void>();
|
||||
focused: boolean = false;
|
||||
private _destroyed: Subject<void> = new Subject<void>();
|
||||
|
||||
@ViewChild('connectionContainer', { static: true }) _connectionContainerRef!: ElementRef;
|
||||
@ViewChild('inputContainer') _inputContainerRef!: ElementRef;
|
||||
@ContentChild(MatFormFieldControl) _controlNonStatic!: MatFormFieldControl<any>;
|
||||
@ContentChild(MatFormFieldControl, { static: true }) _controlStatic!: MatFormFieldControl<any>;
|
||||
get _control(): MatFormFieldControl<any> {
|
||||
return this._explicitFormFieldControl || this._controlNonStatic || this._controlStatic;
|
||||
@ViewChild('connectionContainer', { static: true }) _connectionContainerRef!: ElementRef;
|
||||
@ViewChild('inputContainer') _inputContainerRef!: ElementRef;
|
||||
@ContentChild(MatFormFieldControl) _controlNonStatic!: MatFormFieldControl<any>;
|
||||
@ContentChild(MatFormFieldControl, { static: true }) _controlStatic!: MatFormFieldControl<any>;
|
||||
get _control(): MatFormFieldControl<any> {
|
||||
return this._explicitFormFieldControl || this._controlNonStatic || this._controlStatic;
|
||||
}
|
||||
set _control(value: MatFormFieldControl<any>) {
|
||||
this._explicitFormFieldControl = value;
|
||||
}
|
||||
private _explicitFormFieldControl!: MatFormFieldControl<any>;
|
||||
readonly stateChanges: Subject<void> = new Subject<void>();
|
||||
|
||||
_subscriptAnimationState: string = '';
|
||||
|
||||
@ContentChildren(CNSL_ERROR as any, { descendants: true }) _errorChildren!: QueryList<CnslErrorDirective>;
|
||||
|
||||
@HostListener('blur', ['false'])
|
||||
_focusChanged(isFocused: boolean): void {
|
||||
if (isFocused !== this.focused && (!isFocused)) {
|
||||
this.focused = isFocused;
|
||||
this.stateChanges.next();
|
||||
}
|
||||
set _control(value: MatFormFieldControl<any>) {
|
||||
this._explicitFormFieldControl = value;
|
||||
}
|
||||
private _explicitFormFieldControl!: MatFormFieldControl<any>;
|
||||
readonly stateChanges: Subject<void> = new Subject<void>();
|
||||
}
|
||||
|
||||
_subscriptAnimationState: string = '';
|
||||
constructor(public _elementRef: ElementRef, private _changeDetectorRef: ChangeDetectorRef,
|
||||
@Inject(ElementRef)
|
||||
// Use `ElementRef` here so Angular has something to inject.
|
||||
_labelOptions: any) {
|
||||
super(_elementRef);
|
||||
|
||||
@ContentChildren(CNSL_ERROR as any, { descendants: true }) _errorChildren!: QueryList<CnslErrorDirective>;
|
||||
@ContentChildren(_CNSL_HINT, { descendants: true }) _hintChildren!: QueryList<CnslHintDirective>;
|
||||
}
|
||||
|
||||
@HostListener('blur', ['false'])
|
||||
_focusChanged(isFocused: boolean): void {
|
||||
if (isFocused !== this.focused && (!isFocused)) {
|
||||
this.focused = isFocused;
|
||||
this.stateChanges.next();
|
||||
}
|
||||
public ngAfterViewInit(): void {
|
||||
// Avoid animations on load.
|
||||
this._subscriptAnimationState = 'enter';
|
||||
this._changeDetectorRef.detectChanges();
|
||||
}
|
||||
|
||||
public ngOnDestroy(): void {
|
||||
this._destroyed.next();
|
||||
this._destroyed.complete();
|
||||
}
|
||||
|
||||
public ngAfterContentInit(): void {
|
||||
this._validateControlChild();
|
||||
|
||||
const control = this._control;
|
||||
control.stateChanges.pipe(startWith(null)).subscribe(() => {
|
||||
this._syncDescribedByIds();
|
||||
this._changeDetectorRef.markForCheck();
|
||||
});
|
||||
|
||||
// Run change detection if the value changes.
|
||||
if (control.ngControl && control.ngControl.valueChanges) {
|
||||
control.ngControl.valueChanges
|
||||
.pipe(takeUntil(this._destroyed))
|
||||
.subscribe(() => this._changeDetectorRef.markForCheck());
|
||||
}
|
||||
|
||||
constructor(public _elementRef: ElementRef, private _changeDetectorRef: ChangeDetectorRef,
|
||||
@Inject(ElementRef)
|
||||
// Use `ElementRef` here so Angular has something to inject.
|
||||
_labelOptions: any) {
|
||||
super(_elementRef);
|
||||
// Update the aria-described by when the number of errors changes.
|
||||
this._errorChildren.changes.pipe(startWith(null)).subscribe(() => {
|
||||
this._syncDescribedByIds();
|
||||
this._changeDetectorRef.markForCheck();
|
||||
});
|
||||
}
|
||||
|
||||
/** Throws an error if the form field's control is missing. */
|
||||
protected _validateControlChild(): void {
|
||||
if (!this._control) {
|
||||
throw Error('cnsl-form-field must contain a MatFormFieldControl.');
|
||||
}
|
||||
}
|
||||
|
||||
public ngAfterViewInit(): void {
|
||||
// Avoid animations on load.
|
||||
this._subscriptAnimationState = 'enter';
|
||||
this._changeDetectorRef.detectChanges();
|
||||
private _syncDescribedByIds(): void {
|
||||
if (this._control) {
|
||||
const ids: string[] = [];
|
||||
|
||||
if (this._control.userAriaDescribedBy &&
|
||||
typeof this._control.userAriaDescribedBy === 'string') {
|
||||
ids.push(...this._control.userAriaDescribedBy.split(' '));
|
||||
}
|
||||
|
||||
if (this._errorChildren) {
|
||||
ids.push(...this._errorChildren.map(error => error.id));
|
||||
}
|
||||
|
||||
this._control.setDescribedByIds(ids);
|
||||
}
|
||||
}
|
||||
|
||||
public ngOnDestroy(): void {
|
||||
this._destroyed.next();
|
||||
this._destroyed.complete();
|
||||
}
|
||||
/** Determines whether a class from the NgControl should be forwarded to the host element. */
|
||||
_shouldForward(prop: keyof NgControl): boolean {
|
||||
const ngControl = this._control ? this._control.ngControl : null;
|
||||
return ngControl && ngControl[prop];
|
||||
}
|
||||
|
||||
public ngAfterContentInit(): void {
|
||||
this._validateControlChild();
|
||||
|
||||
const control = this._control;
|
||||
control.stateChanges.pipe(startWith(null)).subscribe(() => {
|
||||
this._syncDescribedByIds();
|
||||
this._changeDetectorRef.markForCheck();
|
||||
});
|
||||
|
||||
// Run change detection if the value changes.
|
||||
if (control.ngControl && control.ngControl.valueChanges) {
|
||||
control.ngControl.valueChanges
|
||||
.pipe(takeUntil(this._destroyed))
|
||||
.subscribe(() => this._changeDetectorRef.markForCheck());
|
||||
}
|
||||
|
||||
// Update the aria-described by when the number of errors changes.
|
||||
this._errorChildren.changes.pipe(startWith(null)).subscribe(() => {
|
||||
this._syncDescribedByIds();
|
||||
this._changeDetectorRef.markForCheck();
|
||||
});
|
||||
}
|
||||
|
||||
/** Throws an error if the form field's control is missing. */
|
||||
protected _validateControlChild(): void {
|
||||
if (!this._control) {
|
||||
throw Error('cnsl-form-field must contain a MatFormFieldControl.');
|
||||
}
|
||||
}
|
||||
|
||||
private _syncDescribedByIds(): void {
|
||||
if (this._control) {
|
||||
const ids: string[] = [];
|
||||
|
||||
if (this._control.userAriaDescribedBy &&
|
||||
typeof this._control.userAriaDescribedBy === 'string') {
|
||||
ids.push(...this._control.userAriaDescribedBy.split(' '));
|
||||
}
|
||||
|
||||
if (this._errorChildren) {
|
||||
ids.push(...this._errorChildren.map(error => error.id));
|
||||
}
|
||||
|
||||
this._control.setDescribedByIds(ids);
|
||||
}
|
||||
}
|
||||
|
||||
/** Determines whether a class from the NgControl should be forwarded to the host element. */
|
||||
_shouldForward(prop: keyof NgControl): boolean {
|
||||
const ngControl = this._control ? this._control.ngControl : null;
|
||||
return ngControl && ngControl[prop];
|
||||
}
|
||||
|
||||
/** Determines whether to display hints or errors. */
|
||||
_getDisplayedMessages(): 'error' | 'hint' {
|
||||
return (this._errorChildren && this._errorChildren.length > 0) ? 'error' : 'hint';
|
||||
}
|
||||
/** Determines whether to display hints or errors. */
|
||||
_getDisplayedMessages(): 'error' | 'hint' {
|
||||
return (this._errorChildren && this._errorChildren.length > 0) ? 'error' : 'hint';
|
||||
}
|
||||
}
|
||||
|
@ -6,25 +6,22 @@ import { LabelModule } from 'src/app/modules/label/label.module';
|
||||
import { LabelComponent } from '../label/label.component';
|
||||
import { CnslErrorDirective } from './error.directive';
|
||||
import { CnslFormFieldComponent } from './form-field.component';
|
||||
import { CnslHintDirective } from './hint.directive';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
CnslFormFieldComponent,
|
||||
CnslErrorDirective,
|
||||
CnslHintDirective,
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
MatRippleModule,
|
||||
LabelModule,
|
||||
],
|
||||
exports: [
|
||||
CnslFormFieldComponent,
|
||||
LabelComponent,
|
||||
CnslErrorDirective,
|
||||
CnslHintDirective,
|
||||
],
|
||||
declarations: [
|
||||
CnslFormFieldComponent,
|
||||
CnslErrorDirective,
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
MatRippleModule,
|
||||
LabelModule,
|
||||
],
|
||||
exports: [
|
||||
CnslFormFieldComponent,
|
||||
LabelComponent,
|
||||
CnslErrorDirective,
|
||||
],
|
||||
})
|
||||
export class FormFieldModule { }
|
||||
|
||||
|
@ -1,33 +0,0 @@
|
||||
import { Directive, InjectionToken, Input } from '@angular/core';
|
||||
|
||||
let nextUniqueId = 0;
|
||||
|
||||
/**
|
||||
* Injection token that can be used to reference instances of `MatHint`. It serves as
|
||||
* alternative token to the actual `MatHint` class which could cause unnecessary
|
||||
* retention of the class and its directive metadata.
|
||||
*
|
||||
* *Note*: This is not part of the public API as the MDC-based form-field will not
|
||||
* need a lightweight token for `MatHint` and we want to reduce breaking changes.
|
||||
*/
|
||||
export const _CNSL_HINT = new InjectionToken<CnslHintDirective>('CnslHintDirective');
|
||||
|
||||
/** Hint text to be shown underneath the form field control. */
|
||||
@Directive({
|
||||
selector: 'cnsl-hint',
|
||||
host: {
|
||||
'class': 'cnsl-hint',
|
||||
'[class.cnsl-form-field-hint-end]': 'align === "end"',
|
||||
'[attr.id]': 'id',
|
||||
// Remove align attribute to prevent it from interfering with layout.
|
||||
'[attr.align]': 'null',
|
||||
},
|
||||
providers: [{ provide: _CNSL_HINT, useExisting: CnslHintDirective }],
|
||||
})
|
||||
export class CnslHintDirective {
|
||||
/** Whether to align the hint label at the start or end of the line. */
|
||||
@Input() align: 'start' | 'end' = 'start';
|
||||
|
||||
/** Unique ID for the hint. Used for the aria-describedby on the form field control. */
|
||||
@Input() id: string = `mat-hint-${nextUniqueId++}`;
|
||||
}
|
@ -17,7 +17,7 @@ import { PolicyComponentServiceType } from '../policies/policy-component-types.e
|
||||
import { JWT, OIDC, RadioItemIdpType } from './idptypes';
|
||||
|
||||
@Component({
|
||||
selector: 'app-idp-create',
|
||||
selector: 'cnsl-idp-create',
|
||||
templateUrl: './idp-create.component.html',
|
||||
styleUrls: ['./idp-create.component.scss'],
|
||||
})
|
||||
|
@ -1,4 +1,4 @@
|
||||
<app-refresh-table [loading]="loading$ | async" (refreshed)="refreshPage()" [dataSize]="dataSource.data.length"
|
||||
<cnsl-refresh-table [loading]="loading$ | async" (refreshed)="refreshPage()" [dataSize]="dataSource.data.length"
|
||||
[emitRefreshOnPreviousRoutes]="['/iam/idp/create']" [timestamp]="idpResult?.details?.viewTimestamp"
|
||||
[selection]="selection">
|
||||
<div actions>
|
||||
@ -23,15 +23,15 @@
|
||||
<mat-checkbox color="primary" (change)="$event ? masterToggle() : null"
|
||||
[checked]="selection.hasValue() && isAllSelected()"
|
||||
[indeterminate]="selection.hasValue() && !isAllSelected()"
|
||||
[disabled]="serviceType==PolicyComponentServiceType.MGMT">
|
||||
[disabled]="serviceType === PolicyComponentServiceType.MGMT">
|
||||
</mat-checkbox>
|
||||
</th>
|
||||
<td mat-cell *matCellDef="let idp">
|
||||
<mat-checkbox color="primary" (click)="$event.stopPropagation()" class="chbox"
|
||||
[disabled]="serviceType==PolicyComponentServiceType.MGMT && idp?.owner == IDPOwnerType.IDP_OWNER_TYPE_SYSTEM"
|
||||
[disabled]="serviceType === PolicyComponentServiceType.MGMT && idp?.owner === IDPOwnerType.IDP_OWNER_TYPE_SYSTEM"
|
||||
(change)="$event ? selection.toggle(idp) : null" [checked]="selection.isSelected(idp)">
|
||||
<img src="../../../assets/images/google.png"
|
||||
*ngIf="idp.stylingType == IDPSTYLINGTYPE.IDPSTYLINGTYPE_GOOGLE" alt="google" />
|
||||
*ngIf="idp.stylingType === IDPSTYLINGTYPE.IDPSTYLINGTYPE_GOOGLE" alt="google" />
|
||||
</mat-checkbox>
|
||||
</td>
|
||||
</ng-container>
|
||||
@ -91,7 +91,7 @@
|
||||
<th mat-header-cell *matHeaderCellDef></th>
|
||||
<td mat-cell *matCellDef="let idp">
|
||||
<button
|
||||
[disabled]="serviceType==PolicyComponentServiceType.MGMT && idp?.providerType == IDPOwnerType.IDP_OWNER_TYPE_ORG"
|
||||
[disabled]="serviceType === PolicyComponentServiceType.MGMT && idp?.providerType === IDPOwnerType.IDP_OWNER_TYPE_ORG"
|
||||
mat-icon-button color="warn" matTooltip="{{'ACTIONS.REMOVE' | translate}}"
|
||||
(click)="removeIdp(idp)">
|
||||
<i class="las la-trash"></i>
|
||||
@ -107,4 +107,4 @@
|
||||
</div>
|
||||
<cnsl-paginator #paginator class="paginator" [timestamp]="idpResult?.details?.viewTimestamp" [length]="idpResult?.details?.totalResult || 0" [pageSize]="10"
|
||||
[pageSizeOptions]="[5, 10, 20]" (page)="changePage($event)"></cnsl-paginator>
|
||||
</app-refresh-table>
|
||||
</cnsl-refresh-table>
|
@ -17,7 +17,7 @@ import { PolicyComponentServiceType } from '../policies/policy-component-types.e
|
||||
import { WarnDialogComponent } from '../warn-dialog/warn-dialog.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-idp-table',
|
||||
selector: 'cnsl-idp-table',
|
||||
templateUrl: './idp-table.component.html',
|
||||
styleUrls: ['./idp-table.component.scss'],
|
||||
})
|
||||
|
@ -1,176 +1,173 @@
|
||||
<app-detail-layout [backRouterLink]="backroutes" [title]="idp?.name"
|
||||
[description]="'IDP.DETAIL.DESCRIPTION' | translate">
|
||||
<div *ngIf="canWrite | async" actions>
|
||||
<button class="actions-trigger" mat-raised-button color="primary" [matMenuTriggerFor]="idpactions">
|
||||
<span>{{'ACTIONS.ACTIONS' | translate}}</span>
|
||||
<mat-icon class="icon">keyboard_arrow_down</mat-icon>
|
||||
<cnsl-detail-layout [backRouterLink]="backroutes" [title]="'IDP.DETAIL.TITLE' | translate"
|
||||
[description]="'IDP.DETAIL.DESCRIPTION' | translate">
|
||||
<div *ngIf="canWrite | async" actions>
|
||||
<button class="actions-trigger" mat-raised-button color="primary" [matMenuTriggerFor]="idpactions">
|
||||
<span>{{'ACTIONS.ACTIONS' | translate}}</span>
|
||||
<mat-icon class="icon">keyboard_arrow_down</mat-icon>
|
||||
</button>
|
||||
<mat-menu #idpactions="matMenu" xPosition="before">
|
||||
|
||||
<button mat-menu-item *ngIf="idp?.state !== IDPState.IDP_STATE_INACTIVE"
|
||||
(click)="changeState(IDPState.IDP_STATE_INACTIVE)">
|
||||
{{'ACTIONS.DEACTIVATE' | translate}}
|
||||
</button>
|
||||
<mat-menu #idpactions="matMenu" xPosition="before">
|
||||
|
||||
<button mat-menu-item
|
||||
*ngIf="idp?.state !== IDPState.IDP_STATE_INACTIVE"
|
||||
(click)="changeState(IDPState.IDP_STATE_INACTIVE)">
|
||||
{{'ACTIONS.DEACTIVATE' | translate}}
|
||||
</button>
|
||||
<button mat-menu-item *ngIf="idp?.state == IDPState.IDP_STATE_INACTIVE"
|
||||
(click)="changeState(IDPState.IDP_STATE_ACTIVE)">
|
||||
{{'ACTIONS.REACTIVATE' | translate}}
|
||||
</button>
|
||||
<button mat-menu-item matTooltip="{{'IDP.DELETE' | translate}}"
|
||||
(click)="deleteIdp()">
|
||||
<span [style.color]="'var(--warn)'">{{'IDP.DELETE_TITLE' | translate}}</span>
|
||||
</button>
|
||||
</mat-menu>
|
||||
</div>
|
||||
<button mat-menu-item *ngIf="idp?.state === IDPState.IDP_STATE_INACTIVE"
|
||||
(click)="changeState(IDPState.IDP_STATE_ACTIVE)">
|
||||
{{'ACTIONS.REACTIVATE' | translate}}
|
||||
</button>
|
||||
<button mat-menu-item matTooltip="{{'IDP.DELETE' | translate}}" (click)="deleteIdp()">
|
||||
<span [style.color]="'var(--warn)'">{{'IDP.DELETE_TITLE' | translate}}</span>
|
||||
</button>
|
||||
</mat-menu>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<div class="container">
|
||||
|
||||
<cnsl-info-row *ngIf="idp" [idp]="idp"></cnsl-info-row>
|
||||
<form class="idp-form" (ngSubmit)="updateIdp()">
|
||||
<ng-container [formGroup]="idpForm">
|
||||
<div class="idp-content">
|
||||
|
||||
<cnsl-form-field appearance="outline" class="formfield">
|
||||
<cnsl-label>{{ 'IDP.NAME' | translate }}</cnsl-label>
|
||||
<input cnslInput formControlName="name" />
|
||||
</cnsl-form-field>
|
||||
<cnsl-form-field class="formfield" appearance="outline">
|
||||
<cnsl-label>{{ 'IDP.STYLE' | translate }}</cnsl-label>
|
||||
<mat-select formControlName="stylingType">
|
||||
<mat-option *ngFor="let field of styleFields" [value]="field">
|
||||
{{ 'IDP.STYLEFIELD.'+field | translate }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</cnsl-form-field>
|
||||
|
||||
<cnsl-info-section class="auto-reg-info">
|
||||
<div>
|
||||
<p class="auto-reg-desc">{{'IDP.AUTOREGISTER_DESC' | translate}}</p>
|
||||
<mat-checkbox formControlName="autoRegister" [disabled]="(canWrite | async) === false">
|
||||
{{'IDP.AUTOREGISTER' | translate}}
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
</cnsl-info-section>
|
||||
</div>
|
||||
</ng-container>
|
||||
<cnsl-info-row *ngIf="idp" [idp]="idp"></cnsl-info-row>
|
||||
<form class="idp-form" (ngSubmit)="updateIdp()">
|
||||
<ng-container [formGroup]="idpForm">
|
||||
<div class="idp-content">
|
||||
|
||||
<div class="btn-wrapper">
|
||||
<button color="primary" mat-raised-button class="continue-button" [disabled]="idpForm.invalid || (canWrite | async) === false"
|
||||
type="submit">
|
||||
{{ 'ACTIONS.SAVE' | translate }}
|
||||
</button>
|
||||
<cnsl-form-field appearance="outline" class="formfield">
|
||||
<cnsl-label>{{ 'IDP.NAME' | translate }}</cnsl-label>
|
||||
<input cnslInput formControlName="name" />
|
||||
</cnsl-form-field>
|
||||
<cnsl-form-field class="formfield" appearance="outline">
|
||||
<cnsl-label>{{ 'IDP.STYLE' | translate }}</cnsl-label>
|
||||
<mat-select formControlName="stylingType">
|
||||
<mat-option *ngFor="let field of styleFields" [value]="field">
|
||||
{{ 'IDP.STYLEFIELD.'+field | translate }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</cnsl-form-field>
|
||||
|
||||
<cnsl-info-section class="auto-reg-info">
|
||||
<div>
|
||||
<p class="auto-reg-desc">{{'IDP.AUTOREGISTER_DESC' | translate}}</p>
|
||||
<mat-checkbox formControlName="autoRegister" [disabled]="(canWrite | async) === false">
|
||||
{{'IDP.AUTOREGISTER' | translate}}
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
</form>
|
||||
</cnsl-info-section>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="idp?.oidcConfig && oidcConfigForm">
|
||||
<h2>{{'IDP.OIDC.TITLE' | translate}}</h2>
|
||||
<p>{{'IDP.OIDC.DESCRIPTION' | translate}}</p>
|
||||
<div class="btn-wrapper">
|
||||
<button color="primary" mat-raised-button class="continue-button"
|
||||
[disabled]="idpForm.invalid || (canWrite | async) === false" type="submit">
|
||||
{{ 'ACTIONS.SAVE' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<form (ngSubmit)="updateOidcConfig()">
|
||||
<ng-container [formGroup]="oidcConfigForm">
|
||||
<div class="idp-content">
|
||||
<cnsl-form-field appearance="outline" class="formfield">
|
||||
<cnsl-label>{{ 'IDP.ISSUER' | translate }}</cnsl-label>
|
||||
<input cnslInput formControlName="issuer" />
|
||||
</cnsl-form-field>
|
||||
<cnsl-form-field appearance="outline" class="formfield">
|
||||
<cnsl-label>{{ 'IDP.CLIENTID' | translate }}</cnsl-label>
|
||||
<input cnslInput formControlName="clientId" />
|
||||
</cnsl-form-field>
|
||||
<mat-checkbox class="idp-desc" [(ngModel)]="showIdSecretSection" [disabled]="(canWrite | async) === false"
|
||||
[ngModelOptions]="{standalone: true}">
|
||||
Update Client Secret
|
||||
</mat-checkbox>
|
||||
<cnsl-form-field appearance="outline" class="formfield" *ngIf="showIdSecretSection">
|
||||
<cnsl-label>{{ 'IDP.CLIENTSECRET' | translate }}</cnsl-label>
|
||||
<input cnslInput formControlName="clientSecret" />
|
||||
</cnsl-form-field>
|
||||
<div class="line">
|
||||
<cnsl-form-field appearance="outline" class="formfield">
|
||||
<cnsl-label>{{ 'IDP.SCOPESLIST' | translate }}</cnsl-label>
|
||||
<ng-container *ngIf="idp?.oidcConfig && oidcConfigForm">
|
||||
<h2>{{'IDP.OIDC.TITLE' | translate}}</h2>
|
||||
<p>{{'IDP.OIDC.DESCRIPTION' | translate}}</p>
|
||||
|
||||
<input cnslInput [matChipInputFor]="chipScopesList"
|
||||
[matChipInputSeparatorKeyCodes]="separatorKeysCodes" [matChipInputAddOnBlur]="true"
|
||||
(matChipInputTokenEnd)="addScope($event)">
|
||||
</cnsl-form-field>
|
||||
<button (click)="addScope($event)" mat-icon-button>
|
||||
<mat-icon>add</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
<cnsl-form-field appearance="outline" class="formfield fullwidth">
|
||||
<mat-chip-list class="chip-list" #chipScopesList aria-label="scope selection">
|
||||
<mat-chip class="chip" *ngFor="let scope of scopesList?.value" selectable="false"
|
||||
removable (removed)="removeScope(scope)" [disabled]="(canWrite | async) === false">
|
||||
{{scope}} <mat-icon matChipRemove>cancel</mat-icon>
|
||||
</mat-chip>
|
||||
</mat-chip-list>
|
||||
</cnsl-form-field>
|
||||
<form (ngSubmit)="updateOidcConfig()">
|
||||
<ng-container [formGroup]="oidcConfigForm">
|
||||
<div class="idp-content">
|
||||
<cnsl-form-field appearance="outline" class="formfield">
|
||||
<cnsl-label>{{ 'IDP.ISSUER' | translate }}</cnsl-label>
|
||||
<input cnslInput formControlName="issuer" />
|
||||
</cnsl-form-field>
|
||||
<cnsl-form-field appearance="outline" class="formfield">
|
||||
<cnsl-label>{{ 'IDP.CLIENTID' | translate }}</cnsl-label>
|
||||
<input cnslInput formControlName="clientId" />
|
||||
</cnsl-form-field>
|
||||
<mat-checkbox class="idp-desc" [(ngModel)]="showIdSecretSection" [disabled]="(canWrite | async) === false"
|
||||
[ngModelOptions]="{standalone: true}">
|
||||
Update Client Secret
|
||||
</mat-checkbox>
|
||||
<cnsl-form-field appearance="outline" class="formfield" *ngIf="showIdSecretSection">
|
||||
<cnsl-label>{{ 'IDP.CLIENTSECRET' | translate }}</cnsl-label>
|
||||
<input cnslInput formControlName="clientSecret" />
|
||||
</cnsl-form-field>
|
||||
<div class="line">
|
||||
<cnsl-form-field appearance="outline" class="formfield">
|
||||
<cnsl-label>{{ 'IDP.SCOPESLIST' | translate }}</cnsl-label>
|
||||
|
||||
<cnsl-form-field class="formfield" appearance="outline">
|
||||
<cnsl-label>{{ 'IDP.IDPDISPLAYNAMMAPPING' | translate }}</cnsl-label>
|
||||
<mat-select formControlName="displayNameMapping">
|
||||
<mat-option *ngFor="let field of mappingFields" [value]="field">
|
||||
{{ 'IDP.MAPPINGFIELD.'+field | translate }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</cnsl-form-field>
|
||||
<cnsl-form-field class="formfield" appearance="outline">
|
||||
<cnsl-label>{{ 'IDP.USERNAMEMAPPING' | translate }}</cnsl-label>
|
||||
<mat-select formControlName="usernameMapping">
|
||||
<mat-option *ngFor="let field of mappingFields" [value]="field">
|
||||
{{ 'IDP.MAPPINGFIELD.'+field | translate }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</cnsl-form-field>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<div class="btn-wrapper">
|
||||
<button color="primary" mat-raised-button class="continue-button"
|
||||
[disabled]="oidcConfigForm.invalid || (canWrite | async) === false" type="submit">
|
||||
{{ 'ACTIONS.SAVE' | translate }}
|
||||
<input cnslInput [matChipInputFor]="chipScopesList" [matChipInputSeparatorKeyCodes]="separatorKeysCodes"
|
||||
[matChipInputAddOnBlur]="true" (matChipInputTokenEnd)="addScope($event)">
|
||||
</cnsl-form-field>
|
||||
<button (click)="addScope($any($event))" mat-icon-button>
|
||||
<mat-icon>add</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
<cnsl-form-field appearance="outline" class="formfield fullwidth">
|
||||
<mat-chip-list class="chip-list" #chipScopesList aria-label="scope selection">
|
||||
<mat-chip class="chip" *ngFor="let scope of scopesList?.value" selectable="false" removable
|
||||
(removed)="removeScope(scope)" [disabled]="(canWrite | async) === false">
|
||||
{{scope}} <mat-icon matChipRemove>cancel</mat-icon>
|
||||
</mat-chip>
|
||||
</mat-chip-list>
|
||||
</cnsl-form-field>
|
||||
|
||||
<cnsl-form-field class="formfield" appearance="outline">
|
||||
<cnsl-label>{{ 'IDP.IDPDISPLAYNAMMAPPING' | translate }}</cnsl-label>
|
||||
<mat-select formControlName="displayNameMapping">
|
||||
<mat-option *ngFor="let field of mappingFields" [value]="field">
|
||||
{{ 'IDP.MAPPINGFIELD.'+field | translate }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</cnsl-form-field>
|
||||
<cnsl-form-field class="formfield" appearance="outline">
|
||||
<cnsl-label>{{ 'IDP.USERNAMEMAPPING' | translate }}</cnsl-label>
|
||||
<mat-select formControlName="usernameMapping">
|
||||
<mat-option *ngFor="let field of mappingFields" [value]="field">
|
||||
{{ 'IDP.MAPPINGFIELD.'+field | translate }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</cnsl-form-field>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="idp?.jwtConfig && jwtConfigForm">
|
||||
<h2>{{'IDP.JWT.TITLE' | translate}}</h2>
|
||||
<p>{{'IDP.JWT.DESCRIPTION' | translate}}</p>
|
||||
<div class="btn-wrapper">
|
||||
<button color="primary" mat-raised-button class="continue-button"
|
||||
[disabled]="oidcConfigForm.invalid || (canWrite | async) === false" type="submit">
|
||||
{{ 'ACTIONS.SAVE' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</ng-container>
|
||||
|
||||
<form (ngSubmit)="updateJwtConfig()">
|
||||
<ng-container [formGroup]="jwtConfigForm">
|
||||
<div class="idp-content">
|
||||
<cnsl-form-field appearance="outline" class="formfield">
|
||||
<cnsl-label>{{ 'IDP.ISSUER' | translate }}</cnsl-label>
|
||||
<input cnslInput formControlName="issuer" />
|
||||
</cnsl-form-field>
|
||||
<ng-container *ngIf="idp?.jwtConfig && jwtConfigForm">
|
||||
<h2>{{'IDP.JWT.TITLE' | translate}}</h2>
|
||||
<p>{{'IDP.JWT.DESCRIPTION' | translate}}</p>
|
||||
|
||||
<cnsl-form-field appearance="outline" class="formfield">
|
||||
<cnsl-label>{{ 'IDP.JWT.HEADERNAME' | translate }}</cnsl-label>
|
||||
<input cnslInput formControlName="headerName" />
|
||||
</cnsl-form-field>
|
||||
<form (ngSubmit)="updateJwtConfig()">
|
||||
<ng-container [formGroup]="jwtConfigForm">
|
||||
<div class="idp-content">
|
||||
<cnsl-form-field appearance="outline" class="formfield">
|
||||
<cnsl-label>{{ 'IDP.ISSUER' | translate }}</cnsl-label>
|
||||
<input cnslInput formControlName="issuer" />
|
||||
</cnsl-form-field>
|
||||
|
||||
<cnsl-form-field appearance="outline" class="formfield">
|
||||
<cnsl-label>{{ 'IDP.JWT.JWTENDPOINT' | translate }}</cnsl-label>
|
||||
<input cnslInput formControlName="jwtEndpoint" />
|
||||
</cnsl-form-field>
|
||||
<cnsl-form-field appearance="outline" class="formfield">
|
||||
<cnsl-label>{{ 'IDP.JWT.HEADERNAME' | translate }}</cnsl-label>
|
||||
<input cnslInput formControlName="headerName" />
|
||||
</cnsl-form-field>
|
||||
|
||||
<cnsl-form-field appearance="outline" class="formfield">
|
||||
<cnsl-label>{{ 'IDP.JWT.JWTKEYSENDPOINT' | translate }}</cnsl-label>
|
||||
<input cnslInput formControlName="keysEndpoint" />
|
||||
</cnsl-form-field>
|
||||
</div>
|
||||
</ng-container>
|
||||
<cnsl-form-field appearance="outline" class="formfield">
|
||||
<cnsl-label>{{ 'IDP.JWT.JWTENDPOINT' | translate }}</cnsl-label>
|
||||
<input cnslInput formControlName="jwtEndpoint" />
|
||||
</cnsl-form-field>
|
||||
|
||||
<div class="btn-wrapper">
|
||||
<button color="primary" mat-raised-button class="continue-button"
|
||||
[disabled]="jwtConfigForm.invalid || (canWrite | async) === false" type="submit">
|
||||
{{ 'ACTIONS.SAVE' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</ng-container>
|
||||
</div>
|
||||
<cnsl-form-field appearance="outline" class="formfield">
|
||||
<cnsl-label>{{ 'IDP.JWT.JWTKEYSENDPOINT' | translate }}</cnsl-label>
|
||||
<input cnslInput formControlName="keysEndpoint" />
|
||||
</cnsl-form-field>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
</app-detail-layout>
|
||||
<div class="btn-wrapper">
|
||||
<button color="primary" mat-raised-button class="continue-button"
|
||||
[disabled]="jwtConfigForm.invalid || (canWrite | async) === false" type="submit">
|
||||
{{ 'ACTIONS.SAVE' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
</cnsl-detail-layout>
|
@ -27,7 +27,7 @@ import { PolicyComponentServiceType } from '../policies/policy-component-types.e
|
||||
import { WarnDialogComponent } from '../warn-dialog/warn-dialog.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-idp',
|
||||
selector: 'cnsl-idp',
|
||||
templateUrl: './idp.component.html',
|
||||
styleUrls: ['./idp.component.scss'],
|
||||
})
|
||||
|
@ -2,27 +2,30 @@
|
||||
<div class="info">
|
||||
<p class="title">{{ 'USER.PAGES.STATE' | translate }}</p>
|
||||
<p *ngIf="user && user.state !== undefined" class="state"
|
||||
[ngClass]="{'active': user.state === UserState.USER_STATE_ACTIVE, 'inactive': user.state === UserState.USER_STATE_INACTIVE}">{{'USER.DATA.STATE'+user.state
|
||||
| translate}}</p>
|
||||
[ngClass]="{'active': user.state === UserState.USER_STATE_ACTIVE, 'inactive': user.state === UserState.USER_STATE_INACTIVE}">
|
||||
{{'USER.DATA.STATE'+user.state
|
||||
| translate}}</p>
|
||||
</div>
|
||||
|
||||
<div class="info">
|
||||
<p class="title">{{ 'USER.DETAILS.DATECREATED' | translate }}</p>
|
||||
<p class="desc">{{user?.details?.creationDate | timestampToDate | localizedDate: 'dd. MMMM YYYY, HH:mm' }}</p>
|
||||
<p *ngIf="user && user.details && user.details.creationDate" class="desc">{{user.details.creationDate |
|
||||
timestampToDate | localizedDate: 'dd. MMMM YYYY, HH:mm' }}</p>
|
||||
</div>
|
||||
|
||||
<div class="info">
|
||||
<p class="title">{{ 'USER.DETAILS.DATECHANGED' | translate }}</p>
|
||||
<p class="desc">{{user?.details?.changeDate | timestampToDate | localizedDate: 'dd. MMMM YYYY, HH:mm' }}</p>
|
||||
<p *ngIf="user && user.details && user.details.changeDate" class="desc">{{user.details.changeDate | timestampToDate
|
||||
| localizedDate: 'dd. MMMM YYYY, HH:mm' }}</p>
|
||||
</div>
|
||||
|
||||
<div class="info width">
|
||||
<p class="title">{{ 'USER.PAGES.LOGINNAMES' | translate }}</p>
|
||||
<div class="copy-row" *ngFor="let login of user?.loginNamesList">
|
||||
<button [disabled]="copied == login"
|
||||
[matTooltip]="(copied != login ? 'ACTIONS.COPY' : 'ACTIONS.COPIED' ) | translate"
|
||||
appCopyToClipboard [valueToCopy]="login" (copiedValue)="copied = $event">
|
||||
{{login}}
|
||||
<button [disabled]="copied === login"
|
||||
[matTooltip]="(copied !== login ? 'ACTIONS.COPY' : 'ACTIONS.COPIED' ) | translate" cnslCopyToClipboard
|
||||
[valueToCopy]="login" (copiedValue)="copied = $event">
|
||||
{{login}}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@ -32,35 +35,38 @@
|
||||
<div class="info">
|
||||
<p class="title">{{ 'APP.PAGES.STATE' | translate }}</p>
|
||||
<p *ngIf="app && app.state !== undefined" class="state"
|
||||
[ngClass]="{'active': app.state === AppState.APP_STATE_ACTIVE, 'inactive': app.state === AppState.APP_STATE_INACTIVE}">{{'APP.PAGES.DETAIL.STATE.'+app.state
|
||||
| translate}}</p>
|
||||
[ngClass]="{'active': app.state === AppState.APP_STATE_ACTIVE, 'inactive': app.state === AppState.APP_STATE_INACTIVE}">
|
||||
{{'APP.PAGES.DETAIL.STATE.'+app.state
|
||||
| translate}}</p>
|
||||
</div>
|
||||
|
||||
<div class="info">
|
||||
<p class="title">{{ 'APP.PAGES.DATECREATED' | translate }}</p>
|
||||
<p class="desc">{{app?.details?.creationDate | timestampToDate | localizedDate: 'dd. MMMM YYYY, HH:mm' }}</p>
|
||||
<p *ngIf="app && app.details && app.details.creationDate" class="desc">{{app.details.creationDate | timestampToDate
|
||||
| localizedDate: 'dd. MMMM YYYY, HH:mm' }}</p>
|
||||
</div>
|
||||
|
||||
<div class="info">
|
||||
<p class="title">{{ 'APP.PAGES.DATECHANGED' | translate }}</p>
|
||||
<p class="desc">{{app?.details?.changeDate | timestampToDate | localizedDate: 'dd. MMMM YYYY, HH:mm' }}</p>
|
||||
<p *ngIf="app && app.details && app.details.changeDate" class="desc">{{app.details.changeDate | timestampToDate |
|
||||
localizedDate: 'dd. MMMM YYYY, HH:mm' }}</p>
|
||||
</div>
|
||||
|
||||
<div class="info" >
|
||||
<div class="info">
|
||||
<p class="title">{{ 'APP.OIDC.INFO.CLIENTID' | translate }}</p>
|
||||
<div class="copy-row" *ngIf="app?.oidcConfig?.clientId">
|
||||
<button [disabled]="copied == app.oidcConfig?.clientId"
|
||||
[matTooltip]="(copied != app.oidcConfig?.clientId ? 'ACTIONS.COPY' : 'ACTIONS.COPIED' ) | translate"
|
||||
appCopyToClipboard [valueToCopy]="app.oidcConfig?.clientId" (copiedValue)="copied = $event">
|
||||
{{app.oidcConfig?.clientId}}
|
||||
<button *ngIf="app.oidcConfig && app.oidcConfig?.clientId" [disabled]="copied === app.oidcConfig?.clientId"
|
||||
[matTooltip]="(copied !== app.oidcConfig?.clientId ? 'ACTIONS.COPY' : 'ACTIONS.COPIED' ) | translate"
|
||||
cnslCopyToClipboard [valueToCopy]="app.oidcConfig.clientId" (copiedValue)="copied = $event">
|
||||
{{app.oidcConfig?.clientId}}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="copy-row" *ngIf="app?.apiConfig?.clientId">
|
||||
<button [disabled]="copied == app.apiConfig?.clientId"
|
||||
[matTooltip]="(copied != app.apiConfig?.clientId ? 'ACTIONS.COPY' : 'ACTIONS.COPIED' ) | translate"
|
||||
appCopyToClipboard [valueToCopy]="app.apiConfig?.clientId" (copiedValue)="copied = $event">
|
||||
{{app.apiConfig?.clientId}}
|
||||
<button *ngIf="app && app.apiConfig && app.apiConfig.clientId" [disabled]="copied === app.apiConfig?.clientId"
|
||||
[matTooltip]="(copied !== app.apiConfig?.clientId ? 'ACTIONS.COPY' : 'ACTIONS.COPIED' ) | translate"
|
||||
cnslCopyToClipboard [valueToCopy]="app.apiConfig.clientId" (copiedValue)="copied = $event">
|
||||
{{app.apiConfig?.clientId}}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@ -68,15 +74,14 @@
|
||||
<div class="info">
|
||||
<p class="title">{{ 'APP.PAGES.URLS' | translate }}</p>
|
||||
<div class="copy-row" *ngFor="let environmentV of (environmentMap | keyvalue)">
|
||||
<div *ngIf="environmentV.value" class="environment">
|
||||
<span class="key">{{environmentV.key}}</span>
|
||||
<button [disabled]="copied == environmentV.value"
|
||||
[matTooltip]="(copied != environmentV.value ? 'ACTIONS.COPY' : 'ACTIONS.COPIED' ) | translate"
|
||||
appCopyToClipboard [valueToCopy]="environmentV.value"
|
||||
(copiedValue)="copied = environmentV.key">
|
||||
{{environmentV.value}}
|
||||
</button>
|
||||
</div>
|
||||
<div *ngIf="environmentV.value" class="environment">
|
||||
<span class="key">{{environmentV.key}}</span>
|
||||
<button [disabled]="copied === environmentV.value"
|
||||
[matTooltip]="(copied !== environmentV.value ? 'ACTIONS.COPY' : 'ACTIONS.COPIED' ) | translate"
|
||||
cnslCopyToClipboard [valueToCopy]="environmentV.value" (copiedValue)="copied = environmentV.key">
|
||||
{{environmentV.value}}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -85,28 +90,31 @@
|
||||
<div class="info width">
|
||||
<p class="title">{{ 'IDP.ID' | translate }}</p>
|
||||
<div class="copy-row">
|
||||
<button [disabled]="copied == idp.id"
|
||||
[matTooltip]="(copied != idp.id ? 'ACTIONS.COPY' : 'ACTIONS.COPIED' ) | translate"
|
||||
appCopyToClipboard [valueToCopy]="idp.id" (copiedValue)="copied = $event">
|
||||
{{idp.id}}
|
||||
<button [disabled]="copied === idp.id"
|
||||
[matTooltip]="(copied !== idp.id ? 'ACTIONS.COPY' : 'ACTIONS.COPIED' ) | translate" cnslCopyToClipboard
|
||||
[valueToCopy]="idp.id" (copiedValue)="copied = $event">
|
||||
{{idp.id}}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info">
|
||||
<p class="title">{{ 'IDP.DETAIL.DATECREATED' | translate }}</p>
|
||||
<p class="desc">{{idp?.details?.creationDate | timestampToDate | localizedDate: 'dd. MMMM YYYY, HH:mm' }}</p>
|
||||
<p class="desc" *ngIf="idp && idp.details && idp.details.creationDate">{{idp.details.creationDate | timestampToDate
|
||||
| localizedDate: 'dd. MMMM YYYY, HH:mm' }}</p>
|
||||
</div>
|
||||
|
||||
<div class="info">
|
||||
<p class="title">{{ 'IDP.DETAIL.DATECHANGED' | translate }}</p>
|
||||
<p class="desc">{{idp?.details?.changeDate | timestampToDate | localizedDate: 'dd. MMMM YYYY, HH:mm' }}</p>
|
||||
<p class="desc" *ngIf="idp && idp.details && idp.details.changeDate">{{idp.details.changeDate | timestampToDate |
|
||||
localizedDate: 'dd. MMMM YYYY, HH:mm' }}</p>
|
||||
</div>
|
||||
|
||||
<div class="info">
|
||||
<p class="title">{{ 'IDP.STATE' | translate }}</p>
|
||||
<p *ngIf="idp && idp.state !== undefined" class="state"
|
||||
[ngClass]="{'active': idp.state === IDPState.IDP_STATE_ACTIVE, 'inactive': idp.state === IDPState.IDP_STATE_INACTIVE}">{{'IDP.STATES.'+idp.state
|
||||
| translate}}</p>
|
||||
[ngClass]="{'active': idp.state === IDPState.IDP_STATE_ACTIVE, 'inactive': idp.state === IDPState.IDP_STATE_INACTIVE}">
|
||||
{{'IDP.STATES.'+idp.state
|
||||
| translate}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -1,6 +1,6 @@
|
||||
<div class="info-section-row" [ngClass]="{'info': type == 'INFO', 'warn': type == 'WARN'}">
|
||||
<i *ngIf="type == 'INFO'" class="icon las la-info"></i>
|
||||
<i *ngIf="type == 'WARN'" class="icon las la-exclamation"></i>
|
||||
<div class="info-section-row" [ngClass]="{'info': type === 'INFO', 'warn': type === 'WARN'}">
|
||||
<i *ngIf="type === 'INFO'" class="icon las la-info"></i>
|
||||
<i *ngIf="type === 'WARN'" class="icon las la-exclamation"></i>
|
||||
|
||||
<div class="info-section-content">
|
||||
<ng-content></ng-content>
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
|
||||
enum InfoSectionType {
|
||||
export enum InfoSectionType {
|
||||
INFO = 'INFO',
|
||||
SUCCESS = 'SUCCESS',
|
||||
WARN = 'WARN',
|
||||
@ -14,5 +14,5 @@ enum InfoSectionType {
|
||||
export class InfoSectionComponent {
|
||||
|
||||
@Input() type: InfoSectionType = InfoSectionType.INFO;
|
||||
@Input() featureLink: string = '';
|
||||
@Input() featureLink: string | string[] = '';
|
||||
}
|
||||
|
@ -184,7 +184,7 @@ export class InputDirective extends _MatInputMixinBase implements MatFormFieldCo
|
||||
* Implemented as part of MatFormFieldControl.
|
||||
* @docs-private
|
||||
*/
|
||||
// tslint:disable-next-line:no-input-rename
|
||||
// eslint-disable-next-line @angular-eslint/no-input-rename
|
||||
@Input('aria-describedby') userAriaDescribedBy!: string;
|
||||
|
||||
/**
|
||||
@ -319,10 +319,10 @@ export class InputDirective extends _MatInputMixinBase implements MatFormFieldCo
|
||||
// In Ivy the `host` bindings will be merged when this class is extended, whereas in
|
||||
// ViewEngine they're overwritten.
|
||||
/** Callback for the cases where the focused state of the input changes. */
|
||||
// tslint:disable:no-host-decorator-in-concrete
|
||||
/* eslint-disable */
|
||||
@HostListener('focus', ['true'])
|
||||
@HostListener('blur', ['false'])
|
||||
// tslint:enable:no-host-decorator-in-concrete
|
||||
/* eslint-enable */
|
||||
_focusChanged(isFocused: boolean): void {
|
||||
if (isFocused !== this.focused && (!this.readonly || !isFocused)) {
|
||||
this.focused = isFocused;
|
||||
@ -333,7 +333,7 @@ export class InputDirective extends _MatInputMixinBase implements MatFormFieldCo
|
||||
// We have to use a `HostListener` here in order to support both Ivy and ViewEngine.
|
||||
// In Ivy the `host` bindings will be merged when this class is extended, whereas in
|
||||
// ViewEngine they're overwritten.
|
||||
// tslint:disable-next-line:no-host-decorator-in-concrete
|
||||
// eslint-disable-next-line
|
||||
@HostListener('input')
|
||||
_onInput(): void {
|
||||
// This is a noop function and is used to let Angular know whenever the value changes.
|
||||
@ -441,7 +441,7 @@ export class InputDirective extends _MatInputMixinBase implements MatFormFieldCo
|
||||
this.focus();
|
||||
}
|
||||
}
|
||||
// tslint:disable
|
||||
/* eslint-disable */
|
||||
static ngAcceptInputType_disabled: BooleanInput;
|
||||
static ngAcceptInputType_readonly: BooleanInput;
|
||||
static ngAcceptInputType_required: BooleanInput;
|
||||
@ -449,5 +449,5 @@ export class InputDirective extends _MatInputMixinBase implements MatFormFieldCo
|
||||
// Accept `any` to avoid conflicts with other directives on `<input>` that may
|
||||
// accept different types.
|
||||
static ngAcceptInputType_value: any;
|
||||
// tslint:enable
|
||||
/* eslint-enable */
|
||||
}
|
||||
|
@ -5,7 +5,7 @@
|
||||
</div>
|
||||
<div class="row">
|
||||
<ng-container *ngFor="let link of links">
|
||||
<ng-template *ngIf="link.withRole" appHasRole [appHasRole]="link.withRole">
|
||||
<ng-template *ngIf="link.withRole" cnslHasRole [hasRole]="link.withRole">
|
||||
<div class="step card">
|
||||
<ng-content select="[icon]"></ng-content>
|
||||
<h6>{{ link.i18nTitle | translate }}</h6>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { Component, Input } from '@angular/core';
|
||||
|
||||
|
||||
export interface CnslLinks {
|
||||
@ -7,7 +7,7 @@ export interface CnslLinks {
|
||||
routerLink?: any;
|
||||
href?: string;
|
||||
iconClasses?: string;
|
||||
withRole?: Array<string | RegExp>;
|
||||
withRole?: string[] | RegExp[];
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -15,11 +15,6 @@ export interface CnslLinks {
|
||||
templateUrl: './links.component.html',
|
||||
styleUrls: ['./links.component.scss'],
|
||||
})
|
||||
export class LinksComponent implements OnInit {
|
||||
export class LinksComponent {
|
||||
@Input() links: Array<CnslLinks> = [];
|
||||
constructor() { }
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
<app-refresh-table [loading]="loading$ | async" (refreshed)="refreshPage()" [dataSize]="dataSource.data.length"
|
||||
<cnsl-refresh-table [loading]="loading$ | async" (refreshed)="refreshPage()" [dataSize]="dataSource.data.length"
|
||||
[timestamp]="keyResult?.details?.viewTimestamp" [selection]="selection">
|
||||
<div actions>
|
||||
<a [disabled]="([('user.write:' + userId), 'user.write'] | hasRole | async) == false" color="primary"
|
||||
<a [disabled]="([('user.write:' + userId), 'user.write'] | hasRole | async) === false" color="primary"
|
||||
mat-raised-button (click)="openAddKey()">
|
||||
<mat-icon class="icon">add</mat-icon>{{ 'ACTIONS.NEW' | translate }}
|
||||
</a>
|
||||
@ -50,7 +50,7 @@
|
||||
<ng-container matColumnDef="actions" stickyEnd>
|
||||
<th mat-header-cell *matHeaderCellDef></th>
|
||||
<td mat-cell *matCellDef="let key">
|
||||
<button [disabled]="([('user.write:' + userId), 'user.write'] | hasRole | async) == false"
|
||||
<button [disabled]="([('user.write:' + userId), 'user.write'] | hasRole | async) === false"
|
||||
mat-icon-button color="warn" matTooltip="{{'ACTIONS.DELETE' | translate}}"
|
||||
(click)="deleteKey(key)">
|
||||
<i class="las la-trash"></i>
|
||||
@ -67,4 +67,4 @@
|
||||
<cnsl-paginator #paginator class="paginator" [timestamp]="keyResult?.details?.viewTimestamp" [length]="keyResult?.details?.totalResult || 0" [pageSize]="10"
|
||||
[pageSizeOptions]="[5, 10, 20]" (page)="changePage($event)"></cnsl-paginator>
|
||||
</div>
|
||||
</app-refresh-table>
|
||||
</cnsl-refresh-table>
|
@ -16,125 +16,125 @@ import { ToastService } from 'src/app/services/toast.service';
|
||||
import { PageEvent, PaginatorComponent } from '../paginator/paginator.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-machine-keys',
|
||||
templateUrl: './machine-keys.component.html',
|
||||
styleUrls: ['./machine-keys.component.scss'],
|
||||
selector: 'cnsl-machine-keys',
|
||||
templateUrl: './machine-keys.component.html',
|
||||
styleUrls: ['./machine-keys.component.scss'],
|
||||
})
|
||||
export class MachineKeysComponent implements OnInit {
|
||||
@Input() userId!: string;
|
||||
@Input() userId!: string;
|
||||
|
||||
@ViewChild(PaginatorComponent) public paginator!: PaginatorComponent;
|
||||
public dataSource: MatTableDataSource<Key.AsObject> = new MatTableDataSource<Key.AsObject>();
|
||||
public selection: SelectionModel<Key.AsObject> = new SelectionModel<Key.AsObject>(true, []);
|
||||
public keyResult!: ListMachineKeysResponse.AsObject;
|
||||
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||
public loading$: Observable<boolean> = this.loadingSubject.asObservable();
|
||||
@Input() public displayedColumns: string[] = ['select', 'id', 'type', 'creationDate', 'expirationDate', 'actions'];
|
||||
@ViewChild(PaginatorComponent) public paginator!: PaginatorComponent;
|
||||
public dataSource: MatTableDataSource<Key.AsObject> = new MatTableDataSource<Key.AsObject>();
|
||||
public selection: SelectionModel<Key.AsObject> = new SelectionModel<Key.AsObject>(true, []);
|
||||
public keyResult!: ListMachineKeysResponse.AsObject;
|
||||
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||
public loading$: Observable<boolean> = this.loadingSubject.asObservable();
|
||||
@Input() public displayedColumns: string[] = ['select', 'id', 'type', 'creationDate', 'expirationDate', 'actions'];
|
||||
|
||||
@Output() public changedSelection: EventEmitter<Array<Key.AsObject>> = new EventEmitter();
|
||||
@Output() public changedSelection: EventEmitter<Array<Key.AsObject>> = new EventEmitter();
|
||||
|
||||
constructor(public translate: TranslateService, private mgmtService: ManagementService, private dialog: MatDialog,
|
||||
private toast: ToastService) {
|
||||
this.selection.changed.subscribe(() => {
|
||||
this.changedSelection.emit(this.selection.selected);
|
||||
});
|
||||
}
|
||||
constructor(public translate: TranslateService, private mgmtService: ManagementService, private dialog: MatDialog,
|
||||
private toast: ToastService) {
|
||||
this.selection.changed.subscribe(() => {
|
||||
this.changedSelection.emit(this.selection.selected);
|
||||
});
|
||||
}
|
||||
|
||||
public ngOnInit(): void {
|
||||
this.getData(10, 0);
|
||||
}
|
||||
public ngOnInit(): void {
|
||||
this.getData(10, 0);
|
||||
}
|
||||
|
||||
|
||||
public isAllSelected(): boolean {
|
||||
const numSelected = this.selection.selected.length;
|
||||
const numRows = this.dataSource.data.length;
|
||||
return numSelected === numRows;
|
||||
}
|
||||
public isAllSelected(): boolean {
|
||||
const numSelected = this.selection.selected.length;
|
||||
const numRows = this.dataSource.data.length;
|
||||
return numSelected === numRows;
|
||||
}
|
||||
|
||||
public masterToggle(): void {
|
||||
this.isAllSelected() ?
|
||||
this.selection.clear() :
|
||||
this.dataSource.data.forEach(row => this.selection.select(row));
|
||||
}
|
||||
public masterToggle(): void {
|
||||
this.isAllSelected() ?
|
||||
this.selection.clear() :
|
||||
this.dataSource.data.forEach(row => this.selection.select(row));
|
||||
}
|
||||
|
||||
|
||||
public changePage(event: PageEvent): void {
|
||||
this.getData(event.pageSize, event.pageIndex * event.pageSize);
|
||||
}
|
||||
public changePage(event: PageEvent): void {
|
||||
this.getData(event.pageSize, event.pageIndex * event.pageSize);
|
||||
}
|
||||
|
||||
public deleteKey(key: Key.AsObject): void {
|
||||
this.mgmtService.removeMachineKey(key.id, this.userId).then(() => {
|
||||
this.selection.clear();
|
||||
this.toast.showInfo('USER.TOAST.SELECTEDKEYSDELETED', true);
|
||||
this.getData(10, 0);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
public deleteKey(key: Key.AsObject): void {
|
||||
this.mgmtService.removeMachineKey(key.id, this.userId).then(() => {
|
||||
this.selection.clear();
|
||||
this.toast.showInfo('USER.TOAST.SELECTEDKEYSDELETED', true);
|
||||
this.getData(10, 0);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
|
||||
public openAddKey(): void {
|
||||
const dialogRef = this.dialog.open(AddKeyDialogComponent, {
|
||||
data: {},
|
||||
width: '400px',
|
||||
});
|
||||
public openAddKey(): void {
|
||||
const dialogRef = this.dialog.open(AddKeyDialogComponent, {
|
||||
data: {},
|
||||
width: '400px',
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe(resp => {
|
||||
if (resp) {
|
||||
const type: KeyType = resp.type;
|
||||
dialogRef.afterClosed().subscribe(resp => {
|
||||
if (resp) {
|
||||
const type: KeyType = resp.type;
|
||||
|
||||
let date: Timestamp | undefined;
|
||||
let date: Timestamp | undefined;
|
||||
|
||||
if (resp.date as Moment) {
|
||||
const ts = new Timestamp();
|
||||
const milliseconds = resp.date.toDate().getTime();
|
||||
const seconds = Math.abs(milliseconds / 1000);
|
||||
const nanos = (milliseconds - seconds * 1000) * 1000 * 1000;
|
||||
ts.setSeconds(seconds);
|
||||
ts.setNanos(nanos);
|
||||
date = ts;
|
||||
}
|
||||
|
||||
if (type) {
|
||||
return this.mgmtService.addMachineKey(this.userId, type, date).then((response) => {
|
||||
if (response) {
|
||||
setTimeout(() => {
|
||||
this.refreshPage();
|
||||
}, 1000);
|
||||
|
||||
this.dialog.open(ShowKeyDialogComponent, {
|
||||
data: {
|
||||
key: response,
|
||||
type: AddKeyDialogType.MACHINE,
|
||||
},
|
||||
width: '400px',
|
||||
});
|
||||
}
|
||||
}).catch((error: any) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private async getData(limit: number, offset: number): Promise<void> {
|
||||
this.loadingSubject.next(true);
|
||||
|
||||
if (this.userId) {
|
||||
this.mgmtService.listMachineKeys(this.userId, limit, offset).then(resp => {
|
||||
this.keyResult = resp;
|
||||
if (resp.resultList) {
|
||||
this.dataSource.data = resp.resultList;
|
||||
}
|
||||
this.loadingSubject.next(false);
|
||||
}).catch((error: any) => {
|
||||
this.toast.showError(error);
|
||||
this.loadingSubject.next(false);
|
||||
});
|
||||
if (resp.date as Moment) {
|
||||
const ts = new Timestamp();
|
||||
const milliseconds = resp.date.toDate().getTime();
|
||||
const seconds = Math.abs(milliseconds / 1000);
|
||||
const nanos = (milliseconds - seconds * 1000) * 1000 * 1000;
|
||||
ts.setSeconds(seconds);
|
||||
ts.setNanos(nanos);
|
||||
date = ts;
|
||||
}
|
||||
}
|
||||
|
||||
public refreshPage(): void {
|
||||
this.getData(this.paginator.pageSize, this.paginator.pageIndex * this.paginator.pageSize);
|
||||
if (type) {
|
||||
this.mgmtService.addMachineKey(this.userId, type, date).then((response) => {
|
||||
if (response) {
|
||||
setTimeout(() => {
|
||||
this.refreshPage();
|
||||
}, 1000);
|
||||
|
||||
this.dialog.open(ShowKeyDialogComponent, {
|
||||
data: {
|
||||
key: response,
|
||||
type: AddKeyDialogType.MACHINE,
|
||||
},
|
||||
width: '400px',
|
||||
});
|
||||
}
|
||||
}).catch((error: any) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private async getData(limit: number, offset: number): Promise<void> {
|
||||
this.loadingSubject.next(true);
|
||||
|
||||
if (this.userId) {
|
||||
this.mgmtService.listMachineKeys(this.userId, limit, offset).then(resp => {
|
||||
this.keyResult = resp;
|
||||
if (resp.resultList) {
|
||||
this.dataSource.data = resp.resultList;
|
||||
}
|
||||
this.loadingSubject.next(false);
|
||||
}).catch((error: any) => {
|
||||
this.toast.showError(error);
|
||||
this.loadingSubject.next(false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public refreshPage(): void {
|
||||
this.getData(this.paginator.pageSize, this.paginator.pageIndex * this.paginator.pageSize);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
<app-refresh-table *ngIf="dataSource" (refreshed)="changePage()" [dataSize]="dataSource.totalResult"
|
||||
<cnsl-refresh-table *ngIf="dataSource" (refreshed)="changePage()" [dataSize]="dataSource.totalResult"
|
||||
[timestamp]="dataSource.viewTimestamp" [selection]="selection" [loading]="dataSource?.loading$ | async">
|
||||
|
||||
<ng-container actions *ngIf="selection.hasValue()">
|
||||
@ -21,9 +21,9 @@
|
||||
<td class="selection" mat-cell *matCellDef="let row">
|
||||
<mat-checkbox [disabled]="!canWrite" color="primary" (click)="$event.stopPropagation()"
|
||||
(change)="$event ? selection.toggle(row) : null" [checked]="selection.isSelected(row)">
|
||||
<app-avatar *ngIf="row?.displayName && row.firstName && row.lastName; else cog" class="avatar"
|
||||
<cnsl-avatar *ngIf="row?.displayName && row.firstName && row.lastName; else cog" class="avatar"
|
||||
[name]="row.displayName" [avatarUrl]="row.avatarUrl || ''" [avatarUrl]="row.avatarUrl|| ''" [forColor]="row?.preferredLoginName" [size]="32">
|
||||
</app-avatar>
|
||||
</cnsl-avatar>
|
||||
<ng-template #cog>
|
||||
<div class="sa-icon">
|
||||
<i class="las la-user-cog"></i>
|
||||
@ -94,4 +94,4 @@
|
||||
<cnsl-paginator *ngIf="dataSource" class="paginator" #paginator [timestamp]="dataSource?.viewTimestamp" [pageSize]="INITIALPAGESIZE"
|
||||
[length]="dataSource.totalResult" [pageSizeOptions]="[25, 50, 100, 250]" (page)="changePage($event)">
|
||||
</cnsl-paginator>
|
||||
</app-refresh-table>
|
||||
</cnsl-refresh-table>
|
@ -6,76 +6,79 @@ import { Observable, Subject } from 'rxjs';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
import { IamMembersDataSource } from 'src/app/pages/iam/iam-members/iam-members-datasource';
|
||||
import { OrgMembersDataSource } from 'src/app/pages/orgs/org-members/org-members-datasource';
|
||||
import {
|
||||
ProjectGrantMembersDataSource,
|
||||
} from 'src/app/pages/projects/owned-projects/project-grant-detail/project-grant-members-datasource';
|
||||
import { Member } from 'src/app/proto/generated/zitadel/member_pb';
|
||||
|
||||
import { PageEvent, PaginatorComponent } from '../paginator/paginator.component';
|
||||
import { ProjectMembersDataSource } from '../project-members/project-members-datasource';
|
||||
|
||||
type MemberDatasource = OrgMembersDataSource | ProjectMembersDataSource | IamMembersDataSource;
|
||||
type MemberDatasource = OrgMembersDataSource | ProjectMembersDataSource | ProjectGrantMembersDataSource | IamMembersDataSource;
|
||||
|
||||
@Component({
|
||||
selector: 'app-members-table',
|
||||
templateUrl: './members-table.component.html',
|
||||
styleUrls: ['./members-table.component.scss'],
|
||||
selector: 'cnsl-members-table',
|
||||
templateUrl: './members-table.component.html',
|
||||
styleUrls: ['./members-table.component.scss'],
|
||||
})
|
||||
export class MembersTableComponent implements OnInit, OnDestroy {
|
||||
public INITIALPAGESIZE: number = 25;
|
||||
@Input() public canDelete: boolean = false;
|
||||
@Input() public canWrite: boolean = false;
|
||||
@ViewChild(PaginatorComponent) public paginator!: PaginatorComponent;
|
||||
@ViewChild(MatTable) public table!: MatTable<Member.AsObject>;
|
||||
@Input() public dataSource!: MemberDatasource;
|
||||
public selection: SelectionModel<any> = new SelectionModel<any>(true, []);
|
||||
@Input() public memberRoleOptions: string[] = [];
|
||||
@Input() public factoryLoadFunc!: Function;
|
||||
@Input() public refreshTrigger!: Observable<void>;
|
||||
@Output() public updateRoles: EventEmitter<{ member: Member.AsObject, change: MatSelectChange; }> = new EventEmitter();
|
||||
@Output() public changedSelection: EventEmitter<any[]> = new EventEmitter();
|
||||
@Output() public deleteMember: EventEmitter<Member.AsObject> = new EventEmitter();
|
||||
public INITIALPAGESIZE: number = 25;
|
||||
@Input() public canDelete: boolean | null = false;
|
||||
@Input() public canWrite: boolean | null = false;
|
||||
@ViewChild(PaginatorComponent) public paginator!: PaginatorComponent;
|
||||
@ViewChild(MatTable) public table!: MatTable<Member.AsObject>;
|
||||
@Input() public dataSource!: MemberDatasource;
|
||||
public selection: SelectionModel<any> = new SelectionModel<any>(true, []);
|
||||
@Input() public memberRoleOptions: string[] = [];
|
||||
@Input() public factoryLoadFunc!: Function;
|
||||
@Input() public refreshTrigger!: Observable<void>;
|
||||
@Output() public updateRoles: EventEmitter<{ member: Member.AsObject, change: MatSelectChange; }> = new EventEmitter();
|
||||
@Output() public changedSelection: EventEmitter<any[]> = new EventEmitter();
|
||||
@Output() public deleteMember: EventEmitter<Member.AsObject> = new EventEmitter();
|
||||
|
||||
private destroyed: Subject<void> = new Subject();
|
||||
private destroyed: Subject<void> = new Subject();
|
||||
|
||||
/** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */
|
||||
public displayedColumns: string[] = ['select', 'userId', 'firstname', 'lastname', 'loginname', 'email', 'roles'];
|
||||
/** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */
|
||||
public displayedColumns: string[] = ['select', 'userId', 'firstname', 'lastname', 'loginname', 'email', 'roles'];
|
||||
|
||||
constructor() {
|
||||
this.selection.changed.pipe(takeUntil(this.destroyed)).subscribe(_ => {
|
||||
this.changedSelection.emit(this.selection.selected);
|
||||
});
|
||||
constructor() {
|
||||
this.selection.changed.pipe(takeUntil(this.destroyed)).subscribe(_ => {
|
||||
this.changedSelection.emit(this.selection.selected);
|
||||
});
|
||||
}
|
||||
|
||||
public ngOnInit(): void {
|
||||
this.refreshTrigger.pipe(takeUntil(this.destroyed)).subscribe(() => {
|
||||
this.changePage(this.paginator);
|
||||
});
|
||||
|
||||
if (this.canDelete) {
|
||||
this.displayedColumns.push('actions');
|
||||
}
|
||||
}
|
||||
|
||||
public ngOnInit(): void {
|
||||
this.refreshTrigger.pipe(takeUntil(this.destroyed)).subscribe(() => {
|
||||
this.changePage(this.paginator);
|
||||
});
|
||||
public ngOnDestroy(): void {
|
||||
this.destroyed.next();
|
||||
}
|
||||
|
||||
if (this.canDelete) {
|
||||
this.displayedColumns.push('actions');
|
||||
}
|
||||
}
|
||||
public isAllSelected(): boolean {
|
||||
const numSelected = this.selection.selected.length;
|
||||
const numRows = this.dataSource.membersSubject.value.length;
|
||||
return numSelected === numRows;
|
||||
}
|
||||
|
||||
public ngOnDestroy(): void {
|
||||
this.destroyed.next();
|
||||
}
|
||||
public masterToggle(): void {
|
||||
this.isAllSelected() ?
|
||||
this.selection.clear() :
|
||||
this.dataSource.membersSubject.value.forEach(row => this.selection.select(row));
|
||||
}
|
||||
|
||||
public isAllSelected(): boolean {
|
||||
const numSelected = this.selection.selected.length;
|
||||
const numRows = this.dataSource.membersSubject.value.length;
|
||||
return numSelected === numRows;
|
||||
}
|
||||
public changePage(event?: PageEvent): any {
|
||||
this.selection.clear();
|
||||
return this.factoryLoadFunc(event ?? this.paginator);
|
||||
}
|
||||
|
||||
public masterToggle(): void {
|
||||
this.isAllSelected() ?
|
||||
this.selection.clear() :
|
||||
this.dataSource.membersSubject.value.forEach(row => this.selection.select(row));
|
||||
}
|
||||
|
||||
public changePage(event?: PageEvent): any {
|
||||
this.selection.clear();
|
||||
return this.factoryLoadFunc(event ?? this.paginator);
|
||||
}
|
||||
|
||||
public triggerDeleteMember(member: any): void {
|
||||
this.deleteMember.emit(member);
|
||||
}
|
||||
public triggerDeleteMember(member: any): void {
|
||||
this.deleteMember.emit(member);
|
||||
}
|
||||
}
|
||||
|
@ -4,19 +4,19 @@ import { Observable } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'app-meta-layout',
|
||||
templateUrl: './meta-layout.component.html',
|
||||
styleUrls: ['./meta-layout.component.scss'],
|
||||
selector: 'cnsl-meta-layout',
|
||||
templateUrl: './meta-layout.component.html',
|
||||
styleUrls: ['./meta-layout.component.scss'],
|
||||
})
|
||||
export class MetaLayoutComponent {
|
||||
|
||||
constructor(private breakpointObserver: BreakpointObserver) {
|
||||
this.isSmallScreen$.subscribe(small => this.hidden = small);
|
||||
}
|
||||
public hidden: boolean = false;
|
||||
public isSmallScreen$: Observable<boolean> = this.breakpointObserver
|
||||
.observe('(max-width: 1000px)')
|
||||
.pipe(map(result => {
|
||||
return result.matches;
|
||||
}));
|
||||
constructor(private breakpointObserver: BreakpointObserver) {
|
||||
this.isSmallScreen$.subscribe(small => this.hidden = small);
|
||||
}
|
||||
public hidden: boolean = false;
|
||||
public isSmallScreen$: Observable<boolean> = this.breakpointObserver
|
||||
.observe('(max-width: 1000px)')
|
||||
.pipe(map(result => {
|
||||
return result.matches;
|
||||
}));
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ import { Component, Inject } from '@angular/core';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
|
||||
@Component({
|
||||
selector: 'app-name-dialog',
|
||||
selector: 'cnsl-name-dialog',
|
||||
templateUrl: './name-dialog.component.html',
|
||||
styleUrls: ['./name-dialog.component.scss'],
|
||||
})
|
||||
|
@ -2,56 +2,56 @@ import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
import { Timestamp } from 'src/app/proto/generated/google/protobuf/timestamp_pb';
|
||||
|
||||
export interface PageEvent {
|
||||
length: number;
|
||||
pageSize: number;
|
||||
pageIndex: number;
|
||||
pageSizeOptions: Array<number>;
|
||||
length: number;
|
||||
pageSize: number;
|
||||
pageIndex: number;
|
||||
pageSizeOptions: Array<number>;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'cnsl-paginator',
|
||||
templateUrl: './paginator.component.html',
|
||||
styleUrls: ['./paginator.component.scss'],
|
||||
selector: 'cnsl-paginator',
|
||||
templateUrl: './paginator.component.html',
|
||||
styleUrls: ['./paginator.component.scss'],
|
||||
})
|
||||
export class PaginatorComponent {
|
||||
@Input() public timestamp!: Timestamp.AsObject;
|
||||
@Input() public length: number = 0;
|
||||
@Input() public pageSize: number = 10;
|
||||
@Input() public pageIndex: number = 0;
|
||||
@Input() public pageSizeOptions: Array<number> = [10, 25, 50];
|
||||
@Output() public page: EventEmitter<PageEvent> = new EventEmitter();
|
||||
constructor() { }
|
||||
@Input() public timestamp: Timestamp.AsObject | undefined = undefined;
|
||||
@Input() public length: number = 0;
|
||||
@Input() public pageSize: number = 10;
|
||||
@Input() public pageIndex: number = 0;
|
||||
@Input() public pageSizeOptions: Array<number> = [10, 25, 50];
|
||||
@Output() public page: EventEmitter<PageEvent> = new EventEmitter();
|
||||
constructor() { }
|
||||
|
||||
public previous(): void {
|
||||
if (this.previousPossible) {
|
||||
this.pageIndex = this.pageIndex - 1;
|
||||
this.emitChange();
|
||||
}
|
||||
public previous(): void {
|
||||
if (this.previousPossible) {
|
||||
this.pageIndex = this.pageIndex - 1;
|
||||
this.emitChange();
|
||||
}
|
||||
}
|
||||
|
||||
public next(): void {
|
||||
if (this.nextPossible) {
|
||||
this.pageIndex = this.pageIndex + 1;
|
||||
this.emitChange();
|
||||
}
|
||||
public next(): void {
|
||||
if (this.nextPossible) {
|
||||
this.pageIndex = this.pageIndex + 1;
|
||||
this.emitChange();
|
||||
}
|
||||
}
|
||||
|
||||
get previousPossible(): boolean {
|
||||
const temp = this.pageIndex - 1;
|
||||
return (temp >= 0);
|
||||
}
|
||||
get previousPossible(): boolean {
|
||||
const temp = this.pageIndex - 1;
|
||||
return (temp >= 0);
|
||||
}
|
||||
|
||||
get nextPossible(): boolean {
|
||||
const temp = this.pageIndex + 1;
|
||||
return (temp <= (this.length / this.pageSize));
|
||||
}
|
||||
get nextPossible(): boolean {
|
||||
const temp = this.pageIndex + 1;
|
||||
return (temp <= (this.length / this.pageSize));
|
||||
}
|
||||
|
||||
public emitChange(): void {
|
||||
this.page.emit({
|
||||
length: this.length,
|
||||
pageSize: this.pageSize,
|
||||
pageIndex: this.pageIndex,
|
||||
pageSizeOptions: this.pageSizeOptions,
|
||||
});
|
||||
}
|
||||
public emitChange(): void {
|
||||
this.page.emit({
|
||||
length: this.length,
|
||||
pageSize: this.pageSize,
|
||||
pageIndex: this.pageIndex,
|
||||
pageSizeOptions: this.pageSizeOptions,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
<div class="validation-col" *ngIf="this.policy">
|
||||
<div class="val" *ngIf="this.policy.minLength">
|
||||
|
||||
<i *ngIf="password?.value?.length == 0; else showSpinner" class="las la-times red"></i>
|
||||
<i *ngIf="password?.value?.length === 0; else showSpinner" class="las la-times red"></i>
|
||||
|
||||
<ng-template #showSpinner>
|
||||
<div *ngIf="(password?.errors?.minlength || password?.value?.length == 0) as currentError; else trueminlength"
|
||||
<div *ngIf="(password?.errors?.minlength || password?.value?.length === 0) as currentError; else trueminlength"
|
||||
class="sp-wrapper">
|
||||
<mat-progress-spinner class="spinner" diameter="20" [color]="currentError ? 'warn': 'valid'"
|
||||
mode="determinate" [value]="(password?.value?.length / policy.minLength) * 100">
|
||||
|
@ -1,18 +1,13 @@
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { FormControl } from '@angular/forms';
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { AbstractControl } from '@angular/forms';
|
||||
import { PasswordComplexityPolicy } from 'src/app/proto/generated/zitadel/policy_pb';
|
||||
|
||||
@Component({
|
||||
selector: 'app-password-complexity-view',
|
||||
templateUrl: './password-complexity-view.component.html',
|
||||
styleUrls: ['./password-complexity-view.component.scss'],
|
||||
selector: 'cnsl-password-complexity-view',
|
||||
templateUrl: './password-complexity-view.component.html',
|
||||
styleUrls: ['./password-complexity-view.component.scss'],
|
||||
})
|
||||
export class PasswordComplexityViewComponent implements OnInit {
|
||||
@Input() public password!: FormControl;
|
||||
@Input() public policy!: PasswordComplexityPolicy.AsObject;
|
||||
constructor() { }
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
export class PasswordComplexityViewComponent {
|
||||
@Input() public password: AbstractControl | null = null;
|
||||
@Input() public policy!: PasswordComplexityPolicy.AsObject;
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
<p class="desc"> {{'LOGINPOLICY.ADDIDP.DESCRIPTION' | translate}}</p>
|
||||
|
||||
<div mat-dialog-content>
|
||||
<cnsl-form-field *ngIf="serviceType == PolicyComponentServiceType.MGMT" class="full-width" appearance="outline">
|
||||
<cnsl-form-field *ngIf="serviceType === PolicyComponentServiceType.MGMT" class="full-width" appearance="outline">
|
||||
<cnsl-label>{{ 'IDP.TYPE' | translate }}</cnsl-label>
|
||||
<mat-select [(ngModel)]="idpType" (selectionChange)="loadIdps()">
|
||||
<mat-option *ngFor="let type of idpTypes" [value]="type">
|
||||
|
@ -8,7 +8,7 @@ import { ToastService } from 'src/app/services/toast.service';
|
||||
import { PolicyComponentServiceType } from '../../../policy-component-types.enum';
|
||||
|
||||
@Component({
|
||||
selector: 'app-add-idp-dialog',
|
||||
selector: 'cnsl-add-idp-dialog',
|
||||
templateUrl: './add-idp-dialog.component.html',
|
||||
styleUrls: ['./add-idp-dialog.component.scss'],
|
||||
})
|
||||
@ -23,7 +23,7 @@ export class AddIdpDialogComponent {
|
||||
];
|
||||
|
||||
public idp: IDP.AsObject | undefined = undefined;
|
||||
public availableIdps: Array<IDP.AsObject[] | IDP.AsObject> | string[] = [];
|
||||
public availableIdps: IDP.AsObject[] = [];
|
||||
|
||||
constructor(
|
||||
private mgmtService: ManagementService,
|
||||
|
@ -1,21 +1,18 @@
|
||||
<div class="idps">
|
||||
<div class="idp mat-elevation-z1"
|
||||
*ngFor="let idp of idps">
|
||||
<img src="../../../assets/images/google.png"
|
||||
*ngIf="idp.idpName.toLowerCase() == 'google'" alt="google" />
|
||||
<div>
|
||||
<span class="name">{{idp.idpName}}</span>
|
||||
<span class="meta-info">{{ 'IDP.TYPES.'+idp.idpType | translate }}</span>
|
||||
<!-- <span class="meta-info">{{ 'IDP.ID' | translate }}: {{idp.idpId}}</span> -->
|
||||
</div>
|
||||
<span class="fill-space"></span>
|
||||
<button color="warn" *ngIf="!disabled" mat-icon-button (click)="removeIdp(idp)" class="rm">
|
||||
<mat-icon matTooltip="{{'ACTIONS.REMOVE' | translate}}">remove_circle</mat-icon>
|
||||
</button>
|
||||
<div class="idp mat-elevation-z1" *ngFor="let idp of idps">
|
||||
<img src="../../../assets/images/google.png" *ngIf="idp.idpName.toLowerCase() === 'google'" alt="google" />
|
||||
<div>
|
||||
<span class="name">{{idp.idpName}}</span>
|
||||
<span class="meta-info">{{ 'IDP.TYPES.'+idp.idpType | translate }}</span>
|
||||
<!-- <span class="meta-info">{{ 'IDP.ID' | translate }}: {{idp.idpId}}</span> -->
|
||||
</div>
|
||||
<span class="fill-space"></span>
|
||||
<button color="warn" *ngIf="!disabled" mat-icon-button (click)="removeIdp(idp)" class="rm">
|
||||
<mat-icon matTooltip="{{'ACTIONS.REMOVE' | translate}}">remove_circle</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
<button mat-raised-button color="primary" [disabled]="disabled"
|
||||
class="new-idp" (click)="openDialog()" >
|
||||
<span>{{'IDP.ADD' | translate}}</span>
|
||||
<mat-icon class="icon">add</mat-icon>
|
||||
</button>
|
||||
<button mat-raised-button color="primary" [disabled]="disabled" class="new-idp" (click)="openDialog()">
|
||||
<span>{{'IDP.ADD' | translate}}</span>
|
||||
<mat-icon class="icon">add</mat-icon>
|
||||
</button>
|
||||
</div>
|
@ -1,177 +1,208 @@
|
||||
<app-detail-layout [backRouterLink]="[ serviceType === PolicyComponentServiceType.ADMIN ? '/iam/policies' : '/org']"
|
||||
[title]="'POLICY.LOGIN_POLICY.TITLE' | translate"
|
||||
[description]="(serviceType==PolicyComponentServiceType.MGMT ? 'POLICY.LOGIN_POLICY.DESCRIPTIONCREATEMGMT' : PolicyComponentServiceType.ADMIN ? 'POLICY.LOGIN_POLICY.DESCRIPTIONCREATEADMIN' : '') | translate">
|
||||
<cnsl-info-section *ngIf="isDefault"> {{'POLICY.DEFAULTLABEL' | translate}}</cnsl-info-section>
|
||||
<cnsl-detail-layout [backRouterLink]="[ serviceType === PolicyComponentServiceType.ADMIN ? '/iam/policies' : '/org']"
|
||||
[title]="'POLICY.LOGIN_POLICY.TITLE' | translate"
|
||||
[description]="(serviceType === PolicyComponentServiceType.MGMT ? 'POLICY.LOGIN_POLICY.DESCRIPTIONCREATEMGMT' : PolicyComponentServiceType.ADMIN ? 'POLICY.LOGIN_POLICY.DESCRIPTIONCREATEADMIN' : '') | translate">
|
||||
<cnsl-info-section *ngIf="isDefault"> {{'POLICY.DEFAULTLABEL' | translate}}</cnsl-info-section>
|
||||
|
||||
<div class="spinner-wr">
|
||||
<mat-spinner diameter="30" *ngIf="loading" color="primary"></mat-spinner>
|
||||
</div>
|
||||
<div class="spinner-wr">
|
||||
<mat-spinner diameter="30" *ngIf="loading" color="primary"></mat-spinner>
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="serviceType === PolicyComponentServiceType.MGMT">
|
||||
<ng-template appHasRole [appHasRole]="['policy.delete']">
|
||||
<button *ngIf="!isDefault" [disabled]="serviceType == PolicyComponentServiceType.MGMT && (['login_policy'] | hasFeature | async) == false" color="primary" matTooltip="{{'POLICY.RESET' | translate}}" color="warn" (click)="removePolicy()"
|
||||
mat-stroked-button>
|
||||
{{'POLICY.RESET' | translate}}
|
||||
</button>
|
||||
</ng-template>
|
||||
<ng-container *ngIf="serviceType === PolicyComponentServiceType.MGMT">
|
||||
<ng-template cnslHasRole [hasRole]="['policy.delete']">
|
||||
<button *ngIf="!isDefault" color="primary" matTooltip="{{'POLICY.RESET' | translate}}" color="warn"
|
||||
(click)="removePolicy()" mat-stroked-button>
|
||||
{{'POLICY.RESET' | translate}}
|
||||
</button>
|
||||
</ng-template>
|
||||
|
||||
<ng-template appHasRole [appHasRole]="['policy.write']">
|
||||
<button *ngIf="isDefault" [disabled]="serviceType == PolicyComponentServiceType.MGMT && (['login_policy'] | hasFeature | async) == false" color="primary" matTooltip="{{'POLICY.CREATECUSTOM' | translate}}" (click)="savePolicy()"
|
||||
mat-raised-button>
|
||||
{{'POLICY.CREATECUSTOM' | translate}}
|
||||
</button>
|
||||
</ng-template>
|
||||
<ng-template cnslHasRole [hasRole]="['policy.write']">
|
||||
<button *ngIf="isDefault" color="primary" matTooltip="{{'POLICY.CREATECUSTOM' | translate}}"
|
||||
(click)="savePolicy()" mat-raised-button>
|
||||
{{'POLICY.CREATECUSTOM' | translate}}
|
||||
</button>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
|
||||
<cnsl-card title="{{ 'IDP.LIST.ACTIVETITLE' | translate }}" [expanded]="true">
|
||||
<cnsl-info-section
|
||||
*ngIf="serviceType === PolicyComponentServiceType.MGMT && (['login_policy.idp'] | hasFeature | async) === false"
|
||||
[featureLink]="['/org/features']" class="info" [type]="InfoSectionType.WARN">
|
||||
<span [innerHTML]="'FEATURES.NOTAVAILABLE' | translate: ({value: 'login_policy.idp'})"></span>
|
||||
</cnsl-info-section>
|
||||
|
||||
<app-card title="{{ 'IDP.LIST.ACTIVETITLE' | translate }}"
|
||||
[expanded]="true">
|
||||
<cnsl-login-policy-idps [serviceType]="serviceType" [service]="service"
|
||||
[disabled]="disabled || (serviceType === PolicyComponentServiceType.MGMT && (['login_policy.idp'] | hasFeature | async) === false)">
|
||||
</cnsl-login-policy-idps>
|
||||
</cnsl-card>
|
||||
|
||||
<cnsl-info-section *ngIf="serviceType == PolicyComponentServiceType.MGMT && (['login_policy.idp'] | hasFeature | async) == false" [featureLink]="['/org/features']" class="info" type="WARN">
|
||||
|
||||
<cnsl-card title="{{ 'MFA.LIST.MULTIFACTORTITLE' | translate }}"
|
||||
description="{{'MFA.LIST.MULTIFACTORDESCRIPTION' | translate}}" [expanded]="false">
|
||||
<cnsl-info-section
|
||||
*ngIf="serviceType === PolicyComponentServiceType.MGMT && (['login_policy.factors'] | hasFeature | async) === false"
|
||||
[featureLink]="['/org/features']" class="info" [type]="InfoSectionType.WARN">
|
||||
<span [innerHTML]="'FEATURES.NOTAVAILABLE' | translate: ({value: 'login_policy.factors'})"></span>
|
||||
</cnsl-info-section>
|
||||
|
||||
<cnsl-mfa-table [service]="service" [serviceType]="serviceType"
|
||||
[componentType]="LoginMethodComponentType.MultiFactor"
|
||||
[disabled]="(([serviceType === PolicyComponentServiceType.ADMIN ? 'iam.policy.write' : serviceType === PolicyComponentServiceType.MGMT ? 'policy.write' : ''] | hasRole | async) === false) || (serviceType === PolicyComponentServiceType.MGMT && (['login_policy.factors'] | hasFeature | async) === false)">
|
||||
</cnsl-mfa-table>
|
||||
</cnsl-card>
|
||||
|
||||
<cnsl-card title="{{ 'MFA.LIST.SECONDFACTORTITLE' | translate }}"
|
||||
description="{{'MFA.LIST.SECONDFACTORDESCRIPTION' | translate}}" [expanded]="false">
|
||||
<cnsl-info-section
|
||||
*ngIf="serviceType === PolicyComponentServiceType.MGMT && (['login_policy.factors'] | hasFeature | async) === false"
|
||||
[featureLink]="['/org/features']" class="info" [type]="InfoSectionType.WARN">
|
||||
<span [innerHTML]="'FEATURES.NOTAVAILABLE' | translate: ({value: 'login_policy.factors'})"></span>
|
||||
</cnsl-info-section>
|
||||
|
||||
<cnsl-mfa-table [service]="service" [serviceType]="serviceType"
|
||||
[componentType]="LoginMethodComponentType.SecondFactor"
|
||||
[disabled]="([serviceType === PolicyComponentServiceType.ADMIN ? 'iam.policy.write' : serviceType === PolicyComponentServiceType.MGMT ? 'policy.write' : ''] | hasRole | async) === false || (serviceType === PolicyComponentServiceType.MGMT && (['login_policy.factors'] | hasFeature | async) === false)">
|
||||
</cnsl-mfa-table>
|
||||
</cnsl-card>
|
||||
|
||||
|
||||
|
||||
<cnsl-card title="{{ 'POLICY.LOGIN_POLICY.ADVANCED' | translate }}" [expanded]="false">
|
||||
<div class="content" *ngIf="loginData">
|
||||
<div class="row">
|
||||
<mat-slide-toggle class="toggle" color="primary"
|
||||
[disabled]="disabled || serviceType === PolicyComponentServiceType.MGMT && (['login_policy.username_login'] | hasFeature | async) === false"
|
||||
ngDefaultControl [(ngModel)]="loginData.allowUsernamePassword">
|
||||
{{'POLICY.DATA.ALLOWUSERNAMEPASSWORD' | translate}}
|
||||
</mat-slide-toggle>
|
||||
|
||||
<cnsl-info-section
|
||||
*ngIf="serviceType === PolicyComponentServiceType.MGMT && (['login_policy.username_login'] | hasFeature | async) === false; else usernameInfo"
|
||||
[featureLink]="['/org/features']" class="info" [type]="InfoSectionType.WARN">
|
||||
<span [innerHTML]="'FEATURES.NOTAVAILABLE' | translate: ({value: 'login_policy.username_login'})"></span>
|
||||
</cnsl-info-section>
|
||||
|
||||
<ng-template #usernameInfo>
|
||||
<cnsl-info-section class="info">
|
||||
{{'POLICY.DATA.ALLOWUSERNAMEPASSWORD_DESC' | translate}}
|
||||
</cnsl-info-section>
|
||||
</ng-template>
|
||||
</div>
|
||||
<div class="row">
|
||||
|
||||
|
||||
<mat-slide-toggle class="toggle" color="primary"
|
||||
[disabled]="disabled || (serviceType === PolicyComponentServiceType.MGMT && (['login_policy.registration'] | hasFeature | async) === false)"
|
||||
ngDefaultControl [(ngModel)]="loginData.allowRegister">
|
||||
{{'POLICY.DATA.ALLOWREGISTER' | translate}}
|
||||
</mat-slide-toggle>
|
||||
|
||||
<cnsl-info-section
|
||||
*ngIf="serviceType === PolicyComponentServiceType.MGMT && (['login_policy.registration'] | hasFeature | async) === false; else regInfo"
|
||||
[featureLink]="['/org/features']" class="info" [type]="InfoSectionType.WARN">
|
||||
<span [innerHTML]="'FEATURES.NOTAVAILABLE' | translate: ({value: 'login_policy.registration'})"></span>
|
||||
</cnsl-info-section>
|
||||
|
||||
<ng-template #regInfo>
|
||||
<cnsl-info-section class="info">
|
||||
{{'POLICY.DATA.ALLOWREGISTER_DESC' | translate}}
|
||||
</cnsl-info-section>
|
||||
</ng-template>
|
||||
</div>
|
||||
<div class="row">
|
||||
<mat-slide-toggle class="toggle" color="primary"
|
||||
[disabled]="disabled || serviceType === PolicyComponentServiceType.MGMT && (['login_policy.idp'] | hasFeature | async) === false"
|
||||
ngDefaultControl [(ngModel)]="loginData.allowExternalIdp">
|
||||
{{'POLICY.DATA.ALLOWEXTERNALIDP' | translate}}
|
||||
</mat-slide-toggle>
|
||||
|
||||
<cnsl-info-section
|
||||
*ngIf="serviceType === PolicyComponentServiceType.MGMT && (['login_policy.idp'] | hasFeature | async) === false; else idpInfo"
|
||||
[featureLink]="['/org/features']" class="info" [type]="InfoSectionType.WARN">
|
||||
<span [innerHTML]="'FEATURES.NOTAVAILABLE' | translate: ({value: 'login_policy.idp'})"></span>
|
||||
</cnsl-info-section>
|
||||
|
||||
<cnsl-login-policy-idps [serviceType]="serviceType" [service]="service" [disabled]="disabled || (serviceType == PolicyComponentServiceType.MGMT && (['login_policy.idp'] | hasFeature | async) == false)"></cnsl-login-policy-idps>
|
||||
</app-card>
|
||||
|
||||
<app-card title="{{ 'MFA.LIST.MULTIFACTORTITLE' | translate }}" description="{{'MFA.LIST.MULTIFACTORDESCRIPTION' | translate}}" [expanded]="false">
|
||||
<cnsl-info-section *ngIf="serviceType == PolicyComponentServiceType.MGMT && (['login_policy.factors'] | hasFeature | async) == false" [featureLink]="['/org/features']" class="info" type="WARN">
|
||||
<ng-template #idpInfo>
|
||||
<cnsl-info-section class="info">
|
||||
{{'POLICY.DATA.ALLOWEXTERNALIDP_DESC' | translate}}
|
||||
</cnsl-info-section>
|
||||
</ng-template>
|
||||
</div>
|
||||
<div class="row">
|
||||
<mat-slide-toggle class="toggle" color="primary"
|
||||
[disabled]="disabled || serviceType === PolicyComponentServiceType.MGMT && (['login_policy.factors'] | hasFeature | async) === false"
|
||||
ngDefaultControl [(ngModel)]="loginData.forceMfa">
|
||||
{{'POLICY.DATA.FORCEMFA' | translate}}
|
||||
</mat-slide-toggle>
|
||||
|
||||
<cnsl-info-section
|
||||
*ngIf="serviceType === PolicyComponentServiceType.MGMT && (['login_policy.factors'] | hasFeature | async) === false; else factorsInfo"
|
||||
[featureLink]="['/org/features']" class="info" [type]="InfoSectionType.WARN">
|
||||
<span [innerHTML]="'FEATURES.NOTAVAILABLE' | translate: ({value: 'login_policy.factors'})"></span>
|
||||
</cnsl-info-section>
|
||||
|
||||
<app-mfa-table [service]="service" [serviceType]="serviceType"
|
||||
[componentType]="LoginMethodComponentType.MultiFactor"
|
||||
[disabled]="(([serviceType == PolicyComponentServiceType.ADMIN ? 'iam.policy.write' : serviceType == PolicyComponentServiceType.MGMT ? 'policy.write' : ''] | hasRole | async) == false) || (serviceType == PolicyComponentServiceType.MGMT && (['login_policy.factors'] | hasFeature | async) == false)">
|
||||
</app-mfa-table>
|
||||
</app-card>
|
||||
|
||||
<app-card title="{{ 'MFA.LIST.SECONDFACTORTITLE' | translate }}" description="{{'MFA.LIST.SECONDFACTORDESCRIPTION' | translate}}" [expanded]="false">
|
||||
<cnsl-info-section *ngIf="serviceType == PolicyComponentServiceType.MGMT && (['login_policy.factors'] | hasFeature | async) == false" [featureLink]="['/org/features']" class="info" type="WARN">
|
||||
<span [innerHTML]="'FEATURES.NOTAVAILABLE' | translate: ({value: 'login_policy.factors'})"></span>
|
||||
</cnsl-info-section>
|
||||
|
||||
<app-mfa-table [service]="service" [serviceType]="serviceType"
|
||||
[componentType]="LoginMethodComponentType.SecondFactor"
|
||||
[disabled]="([serviceType == PolicyComponentServiceType.ADMIN ? 'iam.policy.write' : serviceType == PolicyComponentServiceType.MGMT ? 'policy.write' : ''] | hasRole | async) == false || (serviceType == PolicyComponentServiceType.MGMT && (['login_policy.factors'] | hasFeature | async) == false)">
|
||||
</app-mfa-table>
|
||||
</app-card>
|
||||
|
||||
<app-card title="{{ 'POLICY.LOGIN_POLICY.ADVANCED' | translate }}" [expanded]="false">
|
||||
<div class="content" *ngIf="loginData">
|
||||
<div class="row">
|
||||
<mat-slide-toggle class="toggle" color="primary" [disabled]="disabled || serviceType == PolicyComponentServiceType.MGMT && (['login_policy.username_login'] | hasFeature | async) == false" ngDefaultControl
|
||||
[(ngModel)]="loginData.allowUsernamePassword">
|
||||
{{'POLICY.DATA.ALLOWUSERNAMEPASSWORD' | translate}}
|
||||
</mat-slide-toggle>
|
||||
|
||||
<cnsl-info-section *ngIf="serviceType == PolicyComponentServiceType.MGMT && (['login_policy.username_login'] | hasFeature | async) == false; else usernameInfo" [featureLink]="['/org/features']" class="info" type="WARN">
|
||||
<span [innerHTML]="'FEATURES.NOTAVAILABLE' | translate: ({value: 'login_policy.username_login'})"></span>
|
||||
</cnsl-info-section>
|
||||
|
||||
<ng-template #usernameInfo>
|
||||
<cnsl-info-section class="info">
|
||||
{{'POLICY.DATA.ALLOWUSERNAMEPASSWORD_DESC' | translate}}
|
||||
</cnsl-info-section>
|
||||
</ng-template>
|
||||
</div>
|
||||
<div class="row">
|
||||
<mat-slide-toggle class="toggle" color="primary"
|
||||
[disabled]="disabled || (serviceType == PolicyComponentServiceType.MGMT && (['login_policy.registration'] | hasFeature | async) == false)"
|
||||
ngDefaultControl [(ngModel)]="loginData.allowRegister">
|
||||
{{'POLICY.DATA.ALLOWREGISTER' | translate}}
|
||||
</mat-slide-toggle>
|
||||
|
||||
<cnsl-info-section *ngIf="serviceType == PolicyComponentServiceType.MGMT && (['login_policy.registration'] | hasFeature | async) == false; else regInfo" [featureLink]="['/org/features']" class="info" type="WARN">
|
||||
<span [innerHTML]="'FEATURES.NOTAVAILABLE' | translate: ({value: 'login_policy.registration'})"></span>
|
||||
</cnsl-info-section>
|
||||
|
||||
<ng-template #regInfo>
|
||||
<cnsl-info-section class="info">
|
||||
{{'POLICY.DATA.ALLOWREGISTER_DESC' | translate}}
|
||||
</cnsl-info-section>
|
||||
</ng-template>
|
||||
</div>
|
||||
<div class="row">
|
||||
<mat-slide-toggle class="toggle" color="primary" [disabled]="disabled || serviceType == PolicyComponentServiceType.MGMT && (['login_policy.idp'] | hasFeature | async) == false" ngDefaultControl
|
||||
[(ngModel)]="loginData.allowExternalIdp">
|
||||
{{'POLICY.DATA.ALLOWEXTERNALIDP' | translate}}
|
||||
</mat-slide-toggle>
|
||||
|
||||
<cnsl-info-section *ngIf="serviceType == PolicyComponentServiceType.MGMT && (['login_policy.idp'] | hasFeature | async) == false; else idpInfo" [featureLink]="['/org/features']" class="info" type="WARN">
|
||||
<span [innerHTML]="'FEATURES.NOTAVAILABLE' | translate: ({value: 'login_policy.idp'})"></span>
|
||||
</cnsl-info-section>
|
||||
|
||||
<ng-template #idpInfo>
|
||||
<cnsl-info-section class="info">
|
||||
{{'POLICY.DATA.ALLOWEXTERNALIDP_DESC' | translate}}
|
||||
</cnsl-info-section>
|
||||
</ng-template>
|
||||
</div>
|
||||
<div class="row">
|
||||
<mat-slide-toggle class="toggle" color="primary" [disabled]="disabled || serviceType == PolicyComponentServiceType.MGMT && (['login_policy.factors'] | hasFeature | async) == false" ngDefaultControl
|
||||
[(ngModel)]="loginData.forceMfa">
|
||||
{{'POLICY.DATA.FORCEMFA' | translate}}
|
||||
</mat-slide-toggle>
|
||||
|
||||
<cnsl-info-section *ngIf="serviceType == PolicyComponentServiceType.MGMT && (['login_policy.factors'] | hasFeature | async) == false; else factorsInfo" [featureLink]="['/org/features']" class="info" type="WARN">
|
||||
<span [innerHTML]="'FEATURES.NOTAVAILABLE' | translate: ({value: 'login_policy.factors'})"></span>
|
||||
</cnsl-info-section>
|
||||
|
||||
<ng-template #factorsInfo>
|
||||
<cnsl-info-section class="info">
|
||||
{{'POLICY.DATA.FORCEMFA_DESC' | translate}}
|
||||
</cnsl-info-section>
|
||||
</ng-template>
|
||||
</div>
|
||||
<div class="row">
|
||||
<mat-slide-toggle class="toggle" color="primary" [disabled]="disabled || serviceType == PolicyComponentServiceType.MGMT && (['login_policy.password_reset'] | hasFeature | async) == false" ngDefaultControl
|
||||
[(ngModel)]="loginData.hidePasswordReset">
|
||||
{{'POLICY.DATA.HIDEPASSWORDRESET' | translate}}
|
||||
</mat-slide-toggle>
|
||||
|
||||
<cnsl-info-section *ngIf="serviceType == PolicyComponentServiceType.MGMT && (['login_policy.password_reset'] | hasFeature | async) == false; else passwordResetInfo" [featureLink]="['/org/features']" class="info" type="WARN">
|
||||
<span [innerHTML]="'FEATURES.NOTAVAILABLE' | translate: ({value: 'login_policy.password_reset'})"></span>
|
||||
<ng-template #factorsInfo>
|
||||
<cnsl-info-section class="info">
|
||||
{{'POLICY.DATA.FORCEMFA_DESC' | translate}}
|
||||
</cnsl-info-section>
|
||||
|
||||
<ng-template #passwordResetInfo>
|
||||
<cnsl-info-section class="info">
|
||||
{{'POLICY.DATA.HIDEPASSWORDRESET_DESC' | translate}}
|
||||
</cnsl-info-section>
|
||||
</ng-template>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<cnsl-form-field class="form-field" label="Access Code" required="true">
|
||||
<cnsl-label>{{'LOGINPOLICY.PASSWORDLESS' | translate}}</cnsl-label>
|
||||
<mat-select [(ngModel)]="loginData.passwordlessType"
|
||||
[disabled]="disabled || (serviceType == PolicyComponentServiceType.MGMT && (['login_policy.passwordless'] | hasFeature | async) == false)">
|
||||
<mat-option *ngFor="let pt of passwordlessTypes" [value]="pt">
|
||||
{{'LOGINPOLICY.PASSWORDLESSTYPE.'+pt | translate}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</cnsl-form-field>
|
||||
|
||||
<cnsl-info-section *ngIf="serviceType == PolicyComponentServiceType.MGMT && (['login_policy.passwordless'] | hasFeature | async) == false" [featureLink]="['/org/features']" class="info" type="WARN">
|
||||
<span [innerHTML]="'FEATURES.NOTAVAILABLE' | translate: ({value: 'login_policy.passwordless'})"></span>
|
||||
</cnsl-info-section>
|
||||
</div>
|
||||
</ng-template>
|
||||
</div>
|
||||
</app-card>
|
||||
<div class="row">
|
||||
|
||||
<button [disabled]="disabled" class="save-button" (click)="savePolicy()" color="primary" type="submit"
|
||||
mat-raised-button>{{ 'ACTIONS.SAVE' | translate }}</button>
|
||||
<mat-slide-toggle class="toggle" color="primary"
|
||||
[disabled]="disabled || serviceType === PolicyComponentServiceType.MGMT && (['login_policy.password_reset'] | hasFeature | async) === false"
|
||||
ngDefaultControl [(ngModel)]="loginData.hidePasswordReset">
|
||||
{{'POLICY.DATA.HIDEPASSWORDRESET' | translate}}
|
||||
</mat-slide-toggle>
|
||||
|
||||
<div class="divider"></div>
|
||||
<cnsl-info-section
|
||||
*ngIf="serviceType === PolicyComponentServiceType.MGMT && (['login_policy.password_reset'] | hasFeature | async) === false; else passwordResetInfo"
|
||||
[featureLink]="['/org/features']" class="info" [type]="InfoSectionType.WARN">
|
||||
<span [innerHTML]="'FEATURES.NOTAVAILABLE' | translate: ({value: 'login_policy.password_reset'})"></span>
|
||||
</cnsl-info-section>
|
||||
|
||||
<ng-template appHasRole [appHasRole]="['org.idp.read']">
|
||||
<app-card title="{{ 'IDP.LIST.TITLE' | translate }}" description="{{ 'IDP.LIST.DESCRIPTION' | translate }}"
|
||||
[expanded]="false">
|
||||
<ng-template #passwordResetInfo>
|
||||
<cnsl-info-section class="info">
|
||||
{{'POLICY.DATA.HIDEPASSWORDRESET_DESC' | translate}}
|
||||
</cnsl-info-section>
|
||||
</ng-template>
|
||||
</div>
|
||||
|
||||
<app-idp-table [service]="service" [serviceType]="serviceType"
|
||||
[disabled]="([serviceType == PolicyComponentServiceType.ADMIN ? 'iam.idp.write' : serviceType == PolicyComponentServiceType.MGMT ? 'org.idp.write' : ''] | hasRole | async) == false || ((serviceType == PolicyComponentServiceType.MGMT && (['login_policy.idp'] | hasFeature | async) == false))">
|
||||
</app-idp-table>
|
||||
</app-card>
|
||||
</ng-template>
|
||||
<div class="row">
|
||||
<cnsl-form-field class="form-field" label="Access Code" required="true">
|
||||
<cnsl-label>{{'LOGINPOLICY.PASSWORDLESS' | translate}}</cnsl-label>
|
||||
<mat-select [(ngModel)]="loginData.passwordlessType"
|
||||
[disabled]="disabled || (serviceType === PolicyComponentServiceType.MGMT && (['login_policy.passwordless'] | hasFeature | async) === false)">
|
||||
<mat-option *ngFor="let pt of passwordlessTypes" [value]="pt">
|
||||
{{'LOGINPOLICY.PASSWORDLESSTYPE.'+pt | translate}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</cnsl-form-field>
|
||||
|
||||
<app-policy-grid class="grid" [currentPolicy]="currentPolicy" [type]="serviceType" tagForFilter="security"></app-policy-grid>
|
||||
</app-detail-layout>
|
||||
<cnsl-info-section
|
||||
*ngIf="serviceType === PolicyComponentServiceType.MGMT && (['login_policy.passwordless'] | hasFeature | async) === false"
|
||||
[featureLink]="['/org/features']" class="info" [type]="InfoSectionType.WARN">
|
||||
<span [innerHTML]="'FEATURES.NOTAVAILABLE' | translate: ({value: 'login_policy.passwordless'})"></span>
|
||||
</cnsl-info-section>
|
||||
</div>
|
||||
</div>
|
||||
</cnsl-card>
|
||||
|
||||
<button [disabled]="disabled" class="save-button" (click)="savePolicy()" color="primary" type="submit"
|
||||
mat-raised-button>{{ 'ACTIONS.SAVE' | translate }}</button>
|
||||
|
||||
<div class="divider"></div>
|
||||
|
||||
|
||||
<ng-template cnslHasRole [hasRole]="['org.idp.read']">
|
||||
<cnsl-card title="{{ 'IDP.LIST.TITLE' | translate }}" description="{{ 'IDP.LIST.DESCRIPTION' | translate }}"
|
||||
[expanded]="false">
|
||||
|
||||
<cnsl-idp-table [service]="service" [serviceType]="serviceType"
|
||||
[disabled]="([serviceType === PolicyComponentServiceType.ADMIN ? 'iam.idp.write' : serviceType === PolicyComponentServiceType.MGMT ? 'org.idp.write' : ''] | hasRole | async) === false || ((serviceType === PolicyComponentServiceType.MGMT && (['login_policy.idp'] | hasFeature | async) === false))">
|
||||
</cnsl-idp-table>
|
||||
</cnsl-card>
|
||||
</ng-template>
|
||||
|
||||
<cnsl-policy-grid class="grid" [currentPolicy]="currentPolicy" [type]="serviceType" tagForFilter="security">
|
||||
</cnsl-policy-grid>
|
||||
</cnsl-detail-layout>
|
@ -16,12 +16,13 @@ import { AdminService } from 'src/app/services/admin.service';
|
||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
import { InfoSectionType } from '../../info-section/info-section.component';
|
||||
import { GridPolicy, LOGIN_POLICY } from '../../policy-grid/policies';
|
||||
import { PolicyComponentServiceType } from '../policy-component-types.enum';
|
||||
import { LoginMethodComponentType } from './mfa-table/mfa-table.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-login-policy',
|
||||
selector: 'cnsl-login-policy',
|
||||
templateUrl: './login-policy.component.html',
|
||||
styleUrls: ['./login-policy.component.scss'],
|
||||
})
|
||||
@ -39,6 +40,7 @@ export class LoginPolicyComponent implements OnDestroy {
|
||||
public disabled: boolean = true;
|
||||
|
||||
public currentPolicy: GridPolicy = LOGIN_POLICY;
|
||||
public InfoSectionType: any = InfoSectionType;
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private toast: ToastService,
|
||||
|
@ -6,7 +6,7 @@
|
||||
<cnsl-label>{{'MFA.TYPE' | translate}}</cnsl-label>
|
||||
<mat-select [(ngModel)]="newMfaType">
|
||||
<mat-option *ngFor="let mfa of availableMfaTypes" [value]="mfa">
|
||||
{{(data.componentType == LoginMethodComponentType.SecondFactor ? 'MFA.SECONDFACTORTYPES.':
|
||||
{{(data.componentType === LoginMethodComponentType.SecondFactor ? 'MFA.SECONDFACTORTYPES.':
|
||||
LoginMethodComponentType.MultiFactor ? 'MFA.MULTIFACTORTYPES.': '')+mfa | translate}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
|
@ -3,29 +3,29 @@ import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { MultiFactorType, SecondFactorType } from 'src/app/proto/generated/zitadel/policy_pb';
|
||||
|
||||
enum LoginMethodComponentType {
|
||||
MultiFactor = 1,
|
||||
SecondFactor = 2,
|
||||
MultiFactor = 1,
|
||||
SecondFactor = 2,
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-dialog-add-type',
|
||||
templateUrl: './dialog-add-type.component.html',
|
||||
styleUrls: ['./dialog-add-type.component.scss'],
|
||||
selector: 'cnsl-dialog-add-type',
|
||||
templateUrl: './dialog-add-type.component.html',
|
||||
styleUrls: ['./dialog-add-type.component.scss'],
|
||||
})
|
||||
export class DialogAddTypeComponent {
|
||||
public LoginMethodComponentType: any = LoginMethodComponentType;
|
||||
public availableMfaTypes: Array<MultiFactorType | SecondFactorType> = [];
|
||||
public newMfaType!: MultiFactorType | SecondFactorType;
|
||||
constructor(public dialogRef: MatDialogRef<DialogAddTypeComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public data: any) {
|
||||
this.availableMfaTypes = data.types;
|
||||
}
|
||||
public LoginMethodComponentType: any = LoginMethodComponentType;
|
||||
public availableMfaTypes: Array<MultiFactorType | SecondFactorType> = [];
|
||||
public newMfaType!: MultiFactorType | SecondFactorType;
|
||||
constructor(public dialogRef: MatDialogRef<DialogAddTypeComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public data: any) {
|
||||
this.availableMfaTypes = data.types;
|
||||
}
|
||||
|
||||
public closeDialog(): void {
|
||||
this.dialogRef.close();
|
||||
}
|
||||
public closeDialog(): void {
|
||||
this.dialogRef.close();
|
||||
}
|
||||
|
||||
public closeDialogWithCode(): void {
|
||||
this.dialogRef.close(this.newMfaType);
|
||||
}
|
||||
public closeDialogWithCode(): void {
|
||||
this.dialogRef.close(this.newMfaType);
|
||||
}
|
||||
}
|
||||
|
@ -1,19 +1,19 @@
|
||||
<div class="sp_wrapper">
|
||||
<mat-spinner diameter="30" *ngIf="loading$ | async"></mat-spinner>
|
||||
<mat-spinner diameter="30" *ngIf="loading$ | async"></mat-spinner>
|
||||
</div>
|
||||
<div class="mfa-list">
|
||||
<div class="mfa mat-elevation-z1" *ngFor="let mfa of mfas">
|
||||
<span>
|
||||
{{(componentType == LoginMethodComponentType.SecondFactor ? 'MFA.SECONDFACTORTYPES.':
|
||||
LoginMethodComponentType.MultiFactor ? 'MFA.MULTIFACTORTYPES.': '')+mfa | translate}}
|
||||
</span>
|
||||
<div class="mfa mat-elevation-z1" *ngFor="let mfa of mfas">
|
||||
<span>
|
||||
{{(componentType === LoginMethodComponentType.SecondFactor ? 'MFA.SECONDFACTORTYPES.':
|
||||
LoginMethodComponentType.MultiFactor ? 'MFA.MULTIFACTORTYPES.': '')+mfa | translate}}
|
||||
</span>
|
||||
|
||||
<button color="warn" *ngIf="!disabled" mat-icon-button (click)="removeMfa(mfa)" class="rm">
|
||||
<mat-icon matTooltip="{{'ACTIONS.REMOVE' | translate}}">remove_circle</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
<button mat-raised-button color="primary" class="new-mfa" [disabled]="disabled" (click)="!disabled ? addMfa(): null">
|
||||
<span>{{'ACTIONS.ADD' | translate}}</span>
|
||||
<mat-icon class="icon">add</mat-icon>
|
||||
<button color="warn" *ngIf="!disabled" mat-icon-button (click)="removeMfa(mfa)" class="rm">
|
||||
<mat-icon matTooltip="{{'ACTIONS.REMOVE' | translate}}">remove_circle</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
<button mat-raised-button color="primary" class="new-mfa" [disabled]="disabled" (click)="!disabled ? addMfa(): null">
|
||||
<span>{{'ACTIONS.ADD' | translate}}</span>
|
||||
<mat-icon class="icon">add</mat-icon>
|
||||
</button>
|
||||
</div>
|
@ -30,7 +30,7 @@ export enum LoginMethodComponentType {
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-mfa-table',
|
||||
selector: 'cnsl-mfa-table',
|
||||
templateUrl: './mfa-table.component.html',
|
||||
styleUrls: ['./mfa-table.component.scss'],
|
||||
})
|
||||
|
@ -1,4 +1,4 @@
|
||||
<app-detail-layout [backRouterLink]="[ serviceType === PolicyComponentServiceType.ADMIN ? '/iam/policies' : '/org']"
|
||||
<cnsl-detail-layout [backRouterLink]="[ serviceType === PolicyComponentServiceType.ADMIN ? '/iam/policies' : '/org']"
|
||||
[title]="'POLICY.LOGIN_TEXTS.TITLE' | translate"
|
||||
[description]="'POLICY.LOGIN_TEXTS.DESCRIPTION' | translate">
|
||||
|
||||
@ -35,23 +35,23 @@
|
||||
</cnsl-form-field>
|
||||
</form>
|
||||
|
||||
<cnsl-info-section *ngIf="serviceType == PolicyComponentServiceType.MGMT && (['custom_text.login'] | hasFeature | async) == false" [featureLink]="['/org/features']" class="info" type="WARN">
|
||||
<cnsl-info-section *ngIf="serviceType === PolicyComponentServiceType.MGMT && (['custom_text.login'] | hasFeature | async) === false" [featureLink]="['/org/features']" class="info" [type]="InfoSectionType.WARN">
|
||||
<span [innerHTML]="'FEATURES.NOTAVAILABLE' | translate: ({value: 'custom_text.login'})"></span>
|
||||
</cnsl-info-section>
|
||||
|
||||
<div class="divider"></div>
|
||||
|
||||
<div class="content" >
|
||||
<cnsl-edit-text label="one" [disabled]="serviceType == PolicyComponentServiceType.MGMT && (['custom_text.login'] | hasFeature | async) == false" [default$]="getDefaultInitMessageTextMap$" [current$]="getCustomInitMessageTextMap$" (changedValues)="updateCurrentValues(
|
||||
<cnsl-edit-text label="one" [disabled]="serviceType === PolicyComponentServiceType.MGMT && (['custom_text.login'] | hasFeature | async) === false" [default$]="getDefaultInitMessageTextMap$" [current$]="getCustomInitMessageTextMap$" (changedValues)="updateCurrentValues(
|
||||
$event)"></cnsl-edit-text>
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
<button class="reset-button" [disabled]="serviceType == PolicyComponentServiceType.MGMT && (['custom_text.login'] | hasFeature | async) == false" (click)="resetDefault()" color="warn" type="submit"
|
||||
<button class="reset-button" [disabled]="serviceType === PolicyComponentServiceType.MGMT && (['custom_text.login'] | hasFeature | async) === false" (click)="resetDefault()" color="warn" type="submit"
|
||||
mat-stroked-button><i class="las la-history"></i> {{ 'ACTIONS.RESETDEFAULT' | translate }}</button>
|
||||
<button class="save-button" [disabled]="serviceType == PolicyComponentServiceType.MGMT && (['custom_text.login'] | hasFeature | async) == false" (click)="saveCurrentMessage()" color="primary" type="submit"
|
||||
<button class="save-button" [disabled]="serviceType === PolicyComponentServiceType.MGMT && (['custom_text.login'] | hasFeature | async) === false" (click)="saveCurrentMessage()" color="primary" type="submit"
|
||||
mat-raised-button>{{ 'ACTIONS.SAVE' | translate }}</button>
|
||||
</div>
|
||||
|
||||
<app-policy-grid [currentPolicy]="currentPolicy" [type]="serviceType" tagForFilter="text"></app-policy-grid>
|
||||
</app-detail-layout>
|
||||
<cnsl-policy-grid [currentPolicy]="currentPolicy" [type]="serviceType" tagForFilter="text"></cnsl-policy-grid>
|
||||
</cnsl-detail-layout>
|
||||
|
@ -19,12 +19,13 @@ import { AdminService } from 'src/app/services/admin.service';
|
||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
import { InfoSectionType } from '../../info-section/info-section.component';
|
||||
import { GridPolicy, LOGIN_TEXTS_POLICY } from '../../policy-grid/policies';
|
||||
import { WarnDialogComponent } from '../../warn-dialog/warn-dialog.component';
|
||||
import { PolicyComponentServiceType } from '../policy-component-types.enum';
|
||||
import { mapRequestValues } from './helper';
|
||||
|
||||
// tslint:disable
|
||||
/* eslint-disable */
|
||||
const KeyNamesArray = [
|
||||
'emailVerificationDoneText',
|
||||
'emailVerificationText',
|
||||
@ -61,7 +62,7 @@ const KeyNamesArray = [
|
||||
'passwordlessText',
|
||||
'externalRegistrationUserOverviewText'
|
||||
];
|
||||
// tslint:enable
|
||||
/* eslint-enable */
|
||||
|
||||
const REQUESTMAP = {
|
||||
[PolicyComponentServiceType.MGMT]: {
|
||||
@ -88,7 +89,7 @@ const REQUESTMAP = {
|
||||
},
|
||||
};
|
||||
@Component({
|
||||
selector: 'app-login-texts',
|
||||
selector: 'cnsl-login-texts',
|
||||
templateUrl: './login-texts.component.html',
|
||||
styleUrls: ['./login-texts.component.scss'],
|
||||
})
|
||||
@ -114,6 +115,7 @@ export class LoginTextsComponent implements OnDestroy {
|
||||
public currentPolicy: GridPolicy = LOGIN_TEXTS_POLICY;
|
||||
|
||||
public destroy$: Subject<void> = new Subject();
|
||||
public InfoSectionType: any = InfoSectionType;
|
||||
|
||||
public form: FormGroup = new FormGroup({
|
||||
currentSubMap: new FormControl('emailVerificationDoneText'),
|
||||
|
@ -1,10 +1,10 @@
|
||||
<app-detail-layout [maxWidth]="false" [backRouterLink]="[ serviceType === PolicyComponentServiceType.ADMIN ? '/iam/policies' : '/org']"
|
||||
<cnsl-detail-layout [maxWidth]="false" [backRouterLink]="[ serviceType === PolicyComponentServiceType.ADMIN ? '/iam/policies' : '/org']"
|
||||
[title]="'POLICY.MESSAGE_TEXTS.TITLE' | translate"
|
||||
[description]="'POLICY.MESSAGE_TEXTS.DESCRIPTION' | translate">
|
||||
|
||||
<div class="top-actions">
|
||||
<div class="message-type">
|
||||
<button (click)="setCurrentType(type.value)" [ngClass]="{'active': currentType == type.value}" mat-button *ngFor="let type of MESSAGETYPES | keyvalue">{{'POLICY.MESSAGE_TEXTS.TYPES.'+type.value | translate}}</button>
|
||||
<button (click)="setCurrentType($any(type).value)" [ngClass]="{'active': currentType === type.value}" mat-button *ngFor="let type of MESSAGETYPES | keyvalue">{{'POLICY.MESSAGE_TEXTS.TYPES.'+type.value | translate}}</button>
|
||||
</div>
|
||||
|
||||
<cnsl-form-field class="language">
|
||||
@ -19,23 +19,23 @@
|
||||
</cnsl-form-field>
|
||||
</div>
|
||||
|
||||
<cnsl-info-section *ngIf="serviceType == PolicyComponentServiceType.MGMT && (['custom_text.message'] | hasFeature | async) == false" [featureLink]="['/org/features']" class="info" type="WARN">
|
||||
<cnsl-info-section *ngIf="serviceType === PolicyComponentServiceType.MGMT && (['custom_text.message'] | hasFeature | async) === false" [featureLink]="['/org/features']" class="info" [type]="InfoSectionType.WARN">
|
||||
<span [innerHTML]="'FEATURES.NOTAVAILABLE' | translate: ({value: 'custom_text.message'})"></span>
|
||||
</cnsl-info-section>
|
||||
|
||||
<div class="content" >
|
||||
<cnsl-edit-text [chips]="chips[currentType]" [disabled]="serviceType == PolicyComponentServiceType.MGMT && (['custom_text.message'] | hasFeature | async) == false" label="one" [default$]="getDefaultInitMessageTextMap$" [current$]="getCustomInitMessageTextMap$" (changedValues)="updateCurrentValues(
|
||||
<cnsl-edit-text [chips]="chips[currentType]" [disabled]="serviceType === PolicyComponentServiceType.MGMT && (['custom_text.message'] | hasFeature | async) === false" label="one" [default$]="getDefaultInitMessageTextMap$" [current$]="getCustomInitMessageTextMap$" (changedValues)="updateCurrentValues(
|
||||
$event)"></cnsl-edit-text>
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
<button class="reset-button" [disabled]="serviceType == PolicyComponentServiceType.MGMT && (['custom_text.message'] | hasFeature | async) == false" (click)="resetDefault()" color="warn" type="submit"
|
||||
<button class="reset-button" [disabled]="serviceType === PolicyComponentServiceType.MGMT && (['custom_text.message'] | hasFeature | async) === false" (click)="resetDefault()" color="warn" type="submit"
|
||||
mat-stroked-button><i class="las la-history"></i> {{ 'ACTIONS.RESETDEFAULT' | translate }}</button>
|
||||
<button class="save-button" [disabled]="!updateRequest || serviceType == PolicyComponentServiceType.MGMT && (['custom_text.message'] | hasFeature | async) == false" (click)="saveCurrentMessage()" color="primary" type="submit"
|
||||
<button class="save-button" [disabled]="!updateRequest || serviceType === PolicyComponentServiceType.MGMT && (['custom_text.message'] | hasFeature | async) === false" (click)="saveCurrentMessage()" color="primary" type="submit"
|
||||
mat-raised-button>{{ 'ACTIONS.SAVE' | translate }}</button>
|
||||
</div>
|
||||
|
||||
<div class="divider"></div>
|
||||
|
||||
<app-policy-grid [currentPolicy]="currentPolicy" [type]="serviceType" tagForFilter="text"></app-policy-grid>
|
||||
</app-detail-layout>
|
||||
<cnsl-policy-grid [currentPolicy]="currentPolicy" [type]="serviceType" tagForFilter="text"></cnsl-policy-grid>
|
||||
</cnsl-detail-layout>
|
||||
|
@ -40,6 +40,7 @@ import { AdminService } from 'src/app/services/admin.service';
|
||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
import { InfoSectionType } from '../../info-section/info-section.component';
|
||||
import { GridPolicy, MESSAGE_TEXTS_POLICY } from '../../policy-grid/policies';
|
||||
import { WarnDialogComponent } from '../../warn-dialog/warn-dialog.component';
|
||||
import { PolicyComponentServiceType } from '../policy-component-types.enum';
|
||||
@ -267,7 +268,7 @@ const REQUESTMAP = {
|
||||
},
|
||||
};
|
||||
@Component({
|
||||
selector: 'app-message-texts',
|
||||
selector: 'cnsl-message-texts',
|
||||
templateUrl: './message-texts.component.html',
|
||||
styleUrls: ['./message-texts.component.scss'],
|
||||
})
|
||||
@ -285,6 +286,7 @@ export class MessageTextsComponent implements OnDestroy {
|
||||
|
||||
public updateRequest!: SetCustomInitMessageTextRequest | SetDefaultInitMessageTextRequest;
|
||||
|
||||
public InfoSectionType: any = InfoSectionType;
|
||||
public chips: {
|
||||
[messagetype: string]: Array<{ key: string; value: string; }>;
|
||||
} = {
|
||||
@ -446,6 +448,8 @@ export class MessageTextsComponent implements OnDestroy {
|
||||
return this.stripDetails((this.service as ManagementService).getCustomDomainClaimedMessageText(req));
|
||||
case MESSAGETYPES.PASSWORDLESS:
|
||||
return this.stripDetails((this.service as ManagementService).getCustomPasswordlessRegistrationMessageText(req));
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
} else if (this.serviceType === PolicyComponentServiceType.ADMIN) {
|
||||
switch (type) {
|
||||
@ -461,7 +465,11 @@ export class MessageTextsComponent implements OnDestroy {
|
||||
return this.stripDetails((this.service as AdminService).getCustomDomainClaimedMessageText(req));
|
||||
case MESSAGETYPES.PASSWORDLESS:
|
||||
return this.stripDetails((this.service as AdminService).getCustomPasswordlessRegistrationMessageText(req));
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@ -548,36 +556,36 @@ export class MessageTextsComponent implements OnDestroy {
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe(resp => {
|
||||
if (resp) {
|
||||
if (this.serviceType === PolicyComponentServiceType.MGMT) {
|
||||
const handler = (prom: Promise<any>): Promise<any> => {
|
||||
return prom.then(() => {
|
||||
setTimeout(() => {
|
||||
this.loadData(this.currentType);
|
||||
}, 1000);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
};
|
||||
|
||||
switch (this.currentType) {
|
||||
case MESSAGETYPES.INIT:
|
||||
return handler((this.service as ManagementService).resetCustomInitMessageTextToDefault(this.locale));
|
||||
case MESSAGETYPES.VERIFYPHONE:
|
||||
return handler((this.service as ManagementService).resetCustomVerifyPhoneMessageTextToDefault(this.locale));
|
||||
case MESSAGETYPES.VERIFYEMAIL:
|
||||
return handler((this.service as ManagementService).resetCustomVerifyEmailMessageTextToDefault(this.locale));
|
||||
case MESSAGETYPES.PASSWORDRESET:
|
||||
return handler((this.service as ManagementService).resetCustomPasswordResetMessageTextToDefault(this.locale));
|
||||
case MESSAGETYPES.DOMAINCLAIMED:
|
||||
return handler((this.service as ManagementService).resetCustomDomainClaimedMessageTextToDefault(this.locale));
|
||||
case MESSAGETYPES.DOMAINCLAIMED:
|
||||
return handler((this.service as ManagementService)
|
||||
.resetCustomPasswordlessRegistrationMessageTextToDefault(this.locale));
|
||||
|
||||
}
|
||||
if (resp && this.serviceType === PolicyComponentServiceType.MGMT) {
|
||||
const handler = (prom: Promise<any>): Promise<any> => {
|
||||
return prom.then(() => {
|
||||
setTimeout(() => {
|
||||
this.loadData(this.currentType);
|
||||
}, 1000);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
};
|
||||
|
||||
switch (this.currentType) {
|
||||
case MESSAGETYPES.INIT:
|
||||
return handler((this.service as ManagementService).resetCustomInitMessageTextToDefault(this.locale));
|
||||
case MESSAGETYPES.VERIFYPHONE:
|
||||
return handler((this.service as ManagementService).resetCustomVerifyPhoneMessageTextToDefault(this.locale));
|
||||
case MESSAGETYPES.VERIFYEMAIL:
|
||||
return handler((this.service as ManagementService).resetCustomVerifyEmailMessageTextToDefault(this.locale));
|
||||
case MESSAGETYPES.PASSWORDRESET:
|
||||
return handler((this.service as ManagementService).resetCustomPasswordResetMessageTextToDefault(this.locale));
|
||||
case MESSAGETYPES.DOMAINCLAIMED:
|
||||
return handler((this.service as ManagementService).resetCustomDomainClaimedMessageTextToDefault(this.locale));
|
||||
case MESSAGETYPES.DOMAINCLAIMED:
|
||||
return handler((this.service as ManagementService)
|
||||
.resetCustomPasswordlessRegistrationMessageTextToDefault(this.locale));
|
||||
default:
|
||||
return Promise.reject();
|
||||
}
|
||||
} else {
|
||||
return Promise.reject();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
<app-detail-layout [backRouterLink]="[ serviceType === PolicyComponentServiceType.ADMIN ? '/iam/policies' : '/org']"
|
||||
<cnsl-detail-layout [backRouterLink]="[ serviceType === PolicyComponentServiceType.ADMIN ? '/iam/policies' : '/org']"
|
||||
[title]="'POLICY.IAM_POLICY.TITLE' | translate" [description]="'POLICY.IAM_POLICY.DESCRIPTION' | translate">
|
||||
<cnsl-info-section *ngIf="isDefault"> {{'POLICY.DEFAULTLABEL' | translate}}</cnsl-info-section>
|
||||
|
||||
<ng-template appHasRole [appHasRole]="['iam.policy.delete']">
|
||||
<ng-template cnslHasRole [hasRole]="['iam.policy.delete']">
|
||||
<button *ngIf="serviceType === PolicyComponentServiceType.MGMT && !isDefault"
|
||||
matTooltip="{{'POLICY.RESET' | translate}}" color="warn" (click)="removePolicy()" mat-stroked-button>
|
||||
{{'POLICY.RESET' | translate}}
|
||||
@ -24,5 +24,5 @@
|
||||
}}</button>
|
||||
</div>
|
||||
|
||||
<app-policy-grid [currentPolicy]="currentPolicy" [type]="serviceType" tagForFilter="login"></app-policy-grid>
|
||||
</app-detail-layout>
|
||||
<cnsl-policy-grid [currentPolicy]="currentPolicy" [type]="serviceType" tagForFilter="login"></cnsl-policy-grid>
|
||||
</cnsl-detail-layout>
|
@ -15,7 +15,7 @@ import { GridPolicy, IAM_POLICY } from '../../policy-grid/policies';
|
||||
import { PolicyComponentServiceType } from '../policy-component-types.enum';
|
||||
|
||||
@Component({
|
||||
selector: 'app-org-iam-policy',
|
||||
selector: 'cnsl-org-iam-policy',
|
||||
templateUrl: './org-iam-policy.component.html',
|
||||
styleUrls: ['./org-iam-policy.component.scss'],
|
||||
})
|
||||
@ -65,7 +65,7 @@ export class OrgIamPolicyComponent implements OnDestroy {
|
||||
});
|
||||
}
|
||||
|
||||
private async getData(): Promise<GetCustomOrgIAMPolicyResponse.AsObject | GetOrgIAMPolicyResponse.AsObject | undefined> {
|
||||
private async getData(): Promise<GetCustomOrgIAMPolicyResponse.AsObject | GetOrgIAMPolicyResponse.AsObject | any> {
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
return this.managementService.getOrgIAMPolicy();
|
||||
@ -74,6 +74,8 @@ export class OrgIamPolicyComponent implements OnDestroy {
|
||||
return this.adminService.getCustomOrgIAMPolicy(this.org.id);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return Promise.reject();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
<app-detail-layout [backRouterLink]="[ serviceType === PolicyComponentServiceType.ADMIN ? '/iam/policies' : '/org']"
|
||||
<cnsl-detail-layout [backRouterLink]="[ serviceType === PolicyComponentServiceType.ADMIN ? '/iam/policies' : '/org']"
|
||||
[title]="'POLICY.PWD_AGE.TITLE' | translate" [description]="'POLICY.PWD_AGE.DESCRIPTION' | translate">
|
||||
<ng-template appHasRole [appHasRole]="['policy.delete']">
|
||||
<ng-template cnslHasRole [hasRole]="['policy.delete']">
|
||||
<button *ngIf="serviceType === PolicyComponentServiceType.MGMT && !isDefault"
|
||||
matTooltip="{{'POLICY.RESET' | translate}}" color="warn" (click)="removePolicy()" mat-stroked-button>
|
||||
{{'POLICY.RESET' | translate}}
|
||||
@ -41,4 +41,4 @@
|
||||
<button (click)="savePolicy()" color="primary" type="submit"
|
||||
mat-raised-button>{{ 'ACTIONS.SAVE' | translate }}</button>
|
||||
</div>
|
||||
</app-detail-layout>
|
||||
</cnsl-detail-layout>
|
@ -4,7 +4,7 @@ import { Subscription } from 'rxjs';
|
||||
import { switchMap } from 'rxjs/operators';
|
||||
import { GetPasswordAgePolicyResponse as AdminGetPasswordAgePolicyResponse } from 'src/app/proto/generated/zitadel/admin_pb';
|
||||
import {
|
||||
GetPasswordAgePolicyResponse as MgmtGetPasswordAgePolicyResponse,
|
||||
GetPasswordAgePolicyResponse as MgmtGetPasswordAgePolicyResponse,
|
||||
} from 'src/app/proto/generated/zitadel/management_pb';
|
||||
import { PasswordAgePolicy } from 'src/app/proto/generated/zitadel/policy_pb';
|
||||
import { AdminService } from 'src/app/services/admin.service';
|
||||
@ -15,138 +15,138 @@ import { PolicyComponentServiceType } from '../policy-component-types.enum';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-password-age-policy',
|
||||
templateUrl: './password-age-policy.component.html',
|
||||
styleUrls: ['./password-age-policy.component.scss'],
|
||||
selector: 'cnsl-password-age-policy',
|
||||
templateUrl: './password-age-policy.component.html',
|
||||
styleUrls: ['./password-age-policy.component.scss'],
|
||||
})
|
||||
export class PasswordAgePolicyComponent implements OnDestroy {
|
||||
public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT;
|
||||
public service!: AdminService | ManagementService;
|
||||
public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT;
|
||||
public service!: AdminService | ManagementService;
|
||||
|
||||
public ageData!: PasswordAgePolicy.AsObject | PasswordAgePolicy.AsObject;
|
||||
public ageData!: PasswordAgePolicy.AsObject | PasswordAgePolicy.AsObject;
|
||||
|
||||
private sub: Subscription = new Subscription();
|
||||
private sub: Subscription = new Subscription();
|
||||
|
||||
public PolicyComponentServiceType: any = PolicyComponentServiceType;
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private toast: ToastService,
|
||||
private injector: Injector,
|
||||
) {
|
||||
this.sub = this.route.data.pipe(switchMap(data => {
|
||||
this.serviceType = data.serviceType;
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
this.service = this.injector.get(ManagementService as Type<ManagementService>);
|
||||
break;
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
this.service = this.injector.get(AdminService as Type<AdminService>);
|
||||
break;
|
||||
}
|
||||
public PolicyComponentServiceType: any = PolicyComponentServiceType;
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private toast: ToastService,
|
||||
private injector: Injector,
|
||||
) {
|
||||
this.sub = this.route.data.pipe(switchMap(data => {
|
||||
this.serviceType = data.serviceType;
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
this.service = this.injector.get(ManagementService as Type<ManagementService>);
|
||||
break;
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
this.service = this.injector.get(AdminService as Type<AdminService>);
|
||||
break;
|
||||
}
|
||||
|
||||
return this.route.params;
|
||||
})).subscribe(() => {
|
||||
this.getData().then(resp => {
|
||||
if (resp.policy) {
|
||||
this.ageData = resp.policy;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public ngOnDestroy(): void {
|
||||
this.sub.unsubscribe();
|
||||
}
|
||||
|
||||
private async getData():
|
||||
Promise<MgmtGetPasswordAgePolicyResponse.AsObject | AdminGetPasswordAgePolicyResponse.AsObject> {
|
||||
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
return (this.service as ManagementService).getPasswordAgePolicy();
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
return (this.service as AdminService).getPasswordAgePolicy();
|
||||
return this.route.params;
|
||||
})).subscribe(() => {
|
||||
this.getData().then(resp => {
|
||||
if (resp.policy) {
|
||||
this.ageData = resp.policy;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public removePolicy(): void {
|
||||
if (this.serviceType === PolicyComponentServiceType.MGMT) {
|
||||
(this.service as ManagementService).resetPasswordAgePolicyToDefault().then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.RESETSUCCESS', true);
|
||||
setTimeout(() => {
|
||||
this.getData();
|
||||
}, 1000);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
public ngOnDestroy(): void {
|
||||
this.sub.unsubscribe();
|
||||
}
|
||||
|
||||
public incrementExpireWarnDays(): void {
|
||||
if (this.ageData?.expireWarnDays !== undefined) {
|
||||
this.ageData.expireWarnDays++;
|
||||
}
|
||||
}
|
||||
private async getData():
|
||||
Promise<MgmtGetPasswordAgePolicyResponse.AsObject | AdminGetPasswordAgePolicyResponse.AsObject> {
|
||||
|
||||
public decrementExpireWarnDays(): void {
|
||||
if (this.ageData?.expireWarnDays && this.ageData?.expireWarnDays > 0) {
|
||||
this.ageData.expireWarnDays--;
|
||||
}
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
return (this.service as ManagementService).getPasswordAgePolicy();
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
return (this.service as AdminService).getPasswordAgePolicy();
|
||||
}
|
||||
}
|
||||
|
||||
public incrementMaxAgeDays(): void {
|
||||
if (this.ageData?.maxAgeDays !== undefined) {
|
||||
this.ageData.maxAgeDays++;
|
||||
}
|
||||
public removePolicy(): void {
|
||||
if (this.serviceType === PolicyComponentServiceType.MGMT) {
|
||||
(this.service as ManagementService).resetPasswordAgePolicyToDefault().then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.RESETSUCCESS', true);
|
||||
setTimeout(() => {
|
||||
this.getData();
|
||||
}, 1000);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public decrementMaxAgeDays(): void {
|
||||
if (this.ageData?.maxAgeDays && this.ageData?.maxAgeDays > 0) {
|
||||
this.ageData.maxAgeDays--;
|
||||
}
|
||||
public incrementExpireWarnDays(): void {
|
||||
if (this.ageData?.expireWarnDays !== undefined) {
|
||||
this.ageData.expireWarnDays++;
|
||||
}
|
||||
}
|
||||
|
||||
public savePolicy(): void {
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
if (this.ageData.isDefault) {
|
||||
(this.service as ManagementService).addCustomPasswordAgePolicy(
|
||||
this.ageData.maxAgeDays,
|
||||
this.ageData.expireWarnDays,
|
||||
).then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
} else {
|
||||
(this.service as ManagementService).updateCustomPasswordAgePolicy(
|
||||
this.ageData.maxAgeDays,
|
||||
this.ageData.expireWarnDays,
|
||||
).then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
break;
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
(this.service as AdminService).updatePasswordAgePolicy(
|
||||
this.ageData.maxAgeDays,
|
||||
this.ageData.expireWarnDays,
|
||||
).then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
break;
|
||||
}
|
||||
public decrementExpireWarnDays(): void {
|
||||
if (this.ageData?.expireWarnDays && this.ageData?.expireWarnDays > 0) {
|
||||
this.ageData.expireWarnDays--;
|
||||
}
|
||||
}
|
||||
|
||||
public get isDefault(): boolean {
|
||||
if (this.ageData && this.serviceType === PolicyComponentServiceType.MGMT) {
|
||||
return (this.ageData as PasswordAgePolicy.AsObject).isDefault;
|
||||
public incrementMaxAgeDays(): void {
|
||||
if (this.ageData?.maxAgeDays !== undefined) {
|
||||
this.ageData.maxAgeDays++;
|
||||
}
|
||||
}
|
||||
|
||||
public decrementMaxAgeDays(): void {
|
||||
if (this.ageData?.maxAgeDays && this.ageData?.maxAgeDays > 0) {
|
||||
this.ageData.maxAgeDays--;
|
||||
}
|
||||
}
|
||||
|
||||
public savePolicy(): void {
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
if (this.ageData.isDefault) {
|
||||
(this.service as ManagementService).addCustomPasswordAgePolicy(
|
||||
this.ageData.maxAgeDays,
|
||||
this.ageData.expireWarnDays,
|
||||
).then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
} else {
|
||||
return false;
|
||||
(this.service as ManagementService).updateCustomPasswordAgePolicy(
|
||||
this.ageData.maxAgeDays,
|
||||
this.ageData.expireWarnDays,
|
||||
).then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
break;
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
(this.service as AdminService).updatePasswordAgePolicy(
|
||||
this.ageData.maxAgeDays,
|
||||
this.ageData.expireWarnDays,
|
||||
).then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public get isDefault(): boolean {
|
||||
if (this.ageData && this.serviceType === PolicyComponentServiceType.MGMT) {
|
||||
return (this.ageData as PasswordAgePolicy.AsObject).isDefault;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
<app-detail-layout [backRouterLink]="[ serviceType === PolicyComponentServiceType.ADMIN ? '/iam/policies' : '/org']"
|
||||
<cnsl-detail-layout [backRouterLink]="[ serviceType === PolicyComponentServiceType.ADMIN ? '/iam/policies' : '/org']"
|
||||
[title]="'POLICY.PWD_COMPLEXITY.TITLE' | translate" [description]="'POLICY.PWD_COMPLEXITY.DESCRIPTION' | translate">
|
||||
|
||||
<cnsl-info-section *ngIf="isDefault"> {{'POLICY.DEFAULTLABEL' | translate}}</cnsl-info-section>
|
||||
|
||||
<cnsl-info-section *ngIf="serviceType == PolicyComponentServiceType.MGMT && (['password_complexity_policy'] | hasFeature | async) == false" [featureLink]="['/org/features']" class="info" type="WARN">
|
||||
<cnsl-info-section *ngIf="serviceType === PolicyComponentServiceType.MGMT && (['password_complexity_policy'] | hasFeature | async) === false" [featureLink]="['/org/features']" class="info" [type]="InfoSectionType.WARN">
|
||||
<span [innerHTML]="'FEATURES.NOTAVAILABLE' | translate: ({value: 'password_complexity_policy'})"></span>
|
||||
</cnsl-info-section>
|
||||
|
||||
@ -11,8 +11,8 @@
|
||||
<mat-spinner diameter="30" *ngIf="loading" color="primary"></mat-spinner>
|
||||
</div>
|
||||
|
||||
<ng-template appHasRole [appHasRole]="['policy.delete']">
|
||||
<button *ngIf="serviceType === PolicyComponentServiceType.MGMT && !isDefault" [disabled]="serviceType == PolicyComponentServiceType.MGMT && (['password_complexity_policy'] | hasFeature | async) == false"
|
||||
<ng-template cnslHasRole [hasRole]="['policy.delete']">
|
||||
<button *ngIf="serviceType === PolicyComponentServiceType.MGMT && !isDefault" [disabled]="serviceType === PolicyComponentServiceType.MGMT && (['password_complexity_policy'] | hasFeature | async) === false"
|
||||
matTooltip="{{'POLICY.RESET' | translate}}" color="warn" (click)="removePolicy()" mat-stroked-button>
|
||||
{{'POLICY.RESET' | translate}}
|
||||
</button>
|
||||
@ -24,11 +24,11 @@
|
||||
<span class="left-desc">{{'POLICY.DATA.MINLENGTH' | translate}}</span>
|
||||
<span class="fill-space"></span>
|
||||
<div class="length-wrapper">
|
||||
<button mat-icon-button (click)="decrementLength()" [disabled]="serviceType == PolicyComponentServiceType.MGMT && (['password_complexity_policy'] | hasFeature | async) == false">
|
||||
<button mat-icon-button (click)="decrementLength()" [disabled]="serviceType === PolicyComponentServiceType.MGMT && (['password_complexity_policy'] | hasFeature | async) === false">
|
||||
<mat-icon>remove</mat-icon>
|
||||
</button>
|
||||
<span>{{complexityData?.minLength}}</span>
|
||||
<button mat-icon-button (click)="incrementLength()" [disabled]="serviceType == PolicyComponentServiceType.MGMT && (['password_complexity_policy'] | hasFeature | async) == false">
|
||||
<button mat-icon-button (click)="incrementLength()" [disabled]="serviceType === PolicyComponentServiceType.MGMT && (['password_complexity_policy'] | hasFeature | async) === false">
|
||||
<mat-icon>add</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
@ -37,14 +37,14 @@
|
||||
<mat-icon class="icon" svgIcon="mdi_numeric"></mat-icon>
|
||||
<span class="left-desc">{{'POLICY.DATA.HASNUMBER' | translate}}</span>
|
||||
<span class="fill-space"></span>
|
||||
<mat-slide-toggle color="primary" name="hasNumber" ngDefaultControl [(ngModel)]="complexityData.hasNumber" [disabled]="serviceType == PolicyComponentServiceType.MGMT && (['password_complexity_policy'] | hasFeature | async) == false">
|
||||
<mat-slide-toggle color="primary" name="hasNumber" ngDefaultControl [(ngModel)]="complexityData.hasNumber" [disabled]="serviceType === PolicyComponentServiceType.MGMT && (['password_complexity_policy'] | hasFeature | async) === false">
|
||||
</mat-slide-toggle>
|
||||
</div>
|
||||
<div class="row">
|
||||
<mat-icon class="icon" svgIcon="mdi_symbol"></mat-icon>
|
||||
<span class="left-desc">{{'POLICY.DATA.HASSYMBOL' | translate}}</span>
|
||||
<span class="fill-space"></span>
|
||||
<mat-slide-toggle color="primary" name="hasSymbol" ngDefaultControl [(ngModel)]="complexityData.hasSymbol" [disabled]="serviceType == PolicyComponentServiceType.MGMT && (['password_complexity_policy'] | hasFeature | async) == false">
|
||||
<mat-slide-toggle color="primary" name="hasSymbol" ngDefaultControl [(ngModel)]="complexityData.hasSymbol" [disabled]="serviceType === PolicyComponentServiceType.MGMT && (['password_complexity_policy'] | hasFeature | async) === false">
|
||||
</mat-slide-toggle>
|
||||
</div>
|
||||
<div class="row">
|
||||
@ -52,7 +52,7 @@
|
||||
<span class="left-desc">{{'POLICY.DATA.HASLOWERCASE' | translate}}</span>
|
||||
<span class="fill-space"></span>
|
||||
<mat-slide-toggle color="primary" name="hasLowercase" ngDefaultControl
|
||||
[(ngModel)]="complexityData.hasLowercase" [disabled]="serviceType == PolicyComponentServiceType.MGMT && (['password_complexity_policy'] | hasFeature | async) == false">
|
||||
[(ngModel)]="complexityData.hasLowercase" [disabled]="serviceType === PolicyComponentServiceType.MGMT && (['password_complexity_policy'] | hasFeature | async) === false">
|
||||
</mat-slide-toggle>
|
||||
</div>
|
||||
<div class="row">
|
||||
@ -60,15 +60,15 @@
|
||||
<span class="left-desc">{{'POLICY.DATA.HASUPPERCASE' | translate}}</span>
|
||||
<span class="fill-space"></span>
|
||||
<mat-slide-toggle color="primary" name="hasUppercase" ngDefaultControl
|
||||
[(ngModel)]="complexityData.hasUppercase" [disabled]="serviceType == PolicyComponentServiceType.MGMT && (['password_complexity_policy'] | hasFeature | async) == false">
|
||||
[(ngModel)]="complexityData.hasUppercase" [disabled]="serviceType === PolicyComponentServiceType.MGMT && (['password_complexity_policy'] | hasFeature | async) === false">
|
||||
</mat-slide-toggle>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="btn-container">
|
||||
<button (click)="savePolicy()" [disabled]="serviceType == PolicyComponentServiceType.MGMT && (['password_complexity_policy'] | hasFeature | async) == false" color="primary" type="submit" mat-raised-button>{{ 'ACTIONS.SAVE' | translate
|
||||
<button (click)="savePolicy()" [disabled]="serviceType === PolicyComponentServiceType.MGMT && (['password_complexity_policy'] | hasFeature | async) === false" color="primary" type="submit" mat-raised-button>{{ 'ACTIONS.SAVE' | translate
|
||||
}}</button>
|
||||
</div>
|
||||
|
||||
<app-policy-grid [currentPolicy]="currentPolicy" [type]="serviceType" tagForFilter="security"></app-policy-grid>
|
||||
</app-detail-layout>
|
||||
<cnsl-policy-grid [currentPolicy]="currentPolicy" [type]="serviceType" tagForFilter="security"></cnsl-policy-grid>
|
||||
</cnsl-detail-layout>
|
@ -13,11 +13,12 @@ import { AdminService } from 'src/app/services/admin.service';
|
||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
import { InfoSectionType } from '../../info-section/info-section.component';
|
||||
import { COMPLEXITY_POLICY, GridPolicy } from '../../policy-grid/policies';
|
||||
import { PolicyComponentServiceType } from '../policy-component-types.enum';
|
||||
|
||||
@Component({
|
||||
selector: 'app-password-policy',
|
||||
selector: 'cnsl-password-policy',
|
||||
templateUrl: './password-complexity-policy.component.html',
|
||||
styleUrls: ['./password-complexity-policy.component.scss'],
|
||||
})
|
||||
@ -32,6 +33,7 @@ export class PasswordComplexityPolicyComponent implements OnDestroy {
|
||||
|
||||
public loading: boolean = false;
|
||||
public currentPolicy: GridPolicy = COMPLEXITY_POLICY;
|
||||
public InfoSectionType: any = InfoSectionType;
|
||||
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
|
@ -1,14 +1,14 @@
|
||||
<app-detail-layout [backRouterLink]="[ serviceType === PolicyComponentServiceType.ADMIN ? '/iam/policies' : '/org']"
|
||||
<cnsl-detail-layout [backRouterLink]="[ serviceType === PolicyComponentServiceType.ADMIN ? '/iam/policies' : '/org']"
|
||||
[title]="'POLICY.PWD_LOCKOUT.TITLE' | translate" [description]="'POLICY.PWD_LOCKOUT.DESCRIPTION' | translate">
|
||||
|
||||
<cnsl-info-section class="default" *ngIf="isDefault"> {{'POLICY.DEFAULTLABEL' | translate}}</cnsl-info-section>
|
||||
|
||||
<cnsl-info-section *ngIf="serviceType == PolicyComponentServiceType.MGMT && (['lockout_policy'] | hasFeature | async) == false" [featureLink]="['/org/features']" class="info" type="WARN">
|
||||
<cnsl-info-section *ngIf="serviceType === PolicyComponentServiceType.MGMT && (['lockout_policy'] | hasFeature | async) === false" [featureLink]="['/org/features']" class="info" [type]="InfoSectionType.WARN">
|
||||
<span [innerHTML]="'FEATURES.NOTAVAILABLE' | translate: ({value: 'lockout_policy'})"></span>
|
||||
</cnsl-info-section>
|
||||
|
||||
<ng-template appHasRole [appHasRole]="['policy.delete']">
|
||||
<button [disabled]="serviceType == PolicyComponentServiceType.MGMT && (['lockout_policy'] | hasFeature | async) == false" *ngIf="serviceType === PolicyComponentServiceType.MGMT && !isDefault"
|
||||
<ng-template cnslHasRole [hasRole]="['policy.delete']">
|
||||
<button [disabled]="serviceType === PolicyComponentServiceType.MGMT && (['lockout_policy'] | hasFeature | async) === false" *ngIf="serviceType === PolicyComponentServiceType.MGMT && !isDefault"
|
||||
matTooltip="{{'POLICY.RESET' | translate}}" color="warn" (click)="resetPolicy()" mat-stroked-button>
|
||||
{{'POLICY.RESET' | translate}}
|
||||
</button>
|
||||
@ -19,11 +19,11 @@
|
||||
<span class="left-desc">{{'POLICY.DATA.MAXATTEMPTS' | translate}}</span>
|
||||
<span class="fill-space"></span>
|
||||
<div class="length-wrapper">
|
||||
<button [disabled]="serviceType == PolicyComponentServiceType.MGMT && (['lockout_policy'] | hasFeature | async) == false" mat-icon-button (click)="decrementMaxAttempts()">
|
||||
<button [disabled]="serviceType === PolicyComponentServiceType.MGMT && (['lockout_policy'] | hasFeature | async) === false" mat-icon-button (click)="decrementMaxAttempts()">
|
||||
<mat-icon>remove</mat-icon>
|
||||
</button>
|
||||
<span>{{lockoutData?.maxPasswordAttempts}}</span>
|
||||
<button [disabled]="serviceType == PolicyComponentServiceType.MGMT && (['lockout_policy'] | hasFeature | async) == false" mat-icon-button (click)="incrementMaxAttempts()">
|
||||
<button [disabled]="serviceType === PolicyComponentServiceType.MGMT && (['lockout_policy'] | hasFeature | async) === false" mat-icon-button (click)="incrementMaxAttempts()">
|
||||
<mat-icon>add</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
@ -31,7 +31,7 @@
|
||||
</div>
|
||||
|
||||
<div class="btn-container">
|
||||
<button (click)="savePolicy()" [disabled]="serviceType == PolicyComponentServiceType.MGMT && (['lockout_policy'] | hasFeature | async) == false" color="primary" type="submit" mat-raised-button>{{ 'ACTIONS.SAVE' | translate
|
||||
<button (click)="savePolicy()" [disabled]="serviceType === PolicyComponentServiceType.MGMT && (['lockout_policy'] | hasFeature | async) === false" color="primary" type="submit" mat-raised-button>{{ 'ACTIONS.SAVE' | translate
|
||||
}}</button>
|
||||
</div>
|
||||
</app-detail-layout>
|
||||
</cnsl-detail-layout>
|
@ -12,10 +12,11 @@ import { AdminService } from 'src/app/services/admin.service';
|
||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
import { InfoSectionType } from '../../info-section/info-section.component';
|
||||
import { PolicyComponentServiceType } from '../policy-component-types.enum';
|
||||
|
||||
@Component({
|
||||
selector: 'app-password-lockout-policy',
|
||||
selector: 'cnsl-password-lockout-policy',
|
||||
templateUrl: './password-lockout-policy.component.html',
|
||||
styleUrls: ['./password-lockout-policy.component.scss'],
|
||||
})
|
||||
@ -27,6 +28,7 @@ export class PasswordLockoutPolicyComponent implements OnDestroy {
|
||||
public lockoutData!: LockoutPolicy.AsObject;
|
||||
private sub: Subscription = new Subscription();
|
||||
public PolicyComponentServiceType: any = PolicyComponentServiceType;
|
||||
public InfoSectionType: any = InfoSectionType;
|
||||
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
|
@ -1,10 +1,10 @@
|
||||
<app-detail-layout [backRouterLink]="[ serviceType === PolicyComponentServiceType.ADMIN ? '/iam/policies' : '/org']"
|
||||
<cnsl-detail-layout [backRouterLink]="[ serviceType === PolicyComponentServiceType.ADMIN ? '/iam/policies' : '/org']"
|
||||
[title]="'POLICY.PRIVACY_POLICY.TITLE' | translate"
|
||||
[description]="'POLICY.PRIVACY_POLICY.DESCRIPTION' | translate">
|
||||
|
||||
<cnsl-info-section *ngIf="isDefault"> {{'POLICY.DEFAULTLABEL' | translate}}</cnsl-info-section>
|
||||
|
||||
<cnsl-info-section *ngIf="serviceType == PolicyComponentServiceType.MGMT && (['privacy_policy'] | hasFeature | async) == false" [featureLink]="['/org/features']" class="info" type="WARN">
|
||||
<cnsl-info-section *ngIf="serviceType === PolicyComponentServiceType.MGMT && (['privacy_policy'] | hasFeature | async) === false" [featureLink]="['/org/features']" class="info" [type]="InfoSectionType.WARN">
|
||||
<span [innerHTML]="'FEATURES.NOTAVAILABLE' | translate: ({value: 'privacy_policy'})"></span>
|
||||
</cnsl-info-section>
|
||||
|
||||
@ -25,12 +25,11 @@
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
<button *ngIf="!privacyPolicy?.isDefault" class="reset-button" [disabled]="serviceType == PolicyComponentServiceType.MGMT && (['privacy_policy'] | hasFeature | async) == false" (click)="resetDefault()" color="warn" type="submit"
|
||||
<button *ngIf="!privacyPolicy?.isDefault" class="reset-button" [disabled]="serviceType === PolicyComponentServiceType.MGMT && (['privacy_policy'] | hasFeature | async) === false" (click)="resetDefault()" color="warn" type="submit"
|
||||
mat-stroked-button><i class="las la-history"></i> {{ 'ACTIONS.RESETDEFAULT' | translate }}</button>
|
||||
<button class="save-button" [disabled]="serviceType == PolicyComponentServiceType.MGMT && (['privacy_policy'] | hasFeature | async) == false" (click)="saveCurrentMessage()" color="primary" type="submit"
|
||||
<button class="save-button" [disabled]="serviceType === PolicyComponentServiceType.MGMT && (['privacy_policy'] | hasFeature | async) === false" (click)="saveCurrentMessage()" color="primary" type="submit"
|
||||
mat-raised-button>{{ 'ACTIONS.SAVE' | translate }}</button>
|
||||
</div>
|
||||
|
||||
<app-policy-grid [currentPolicy]="currentPolicy" [type]="serviceType" tagForFilter="text"></app-policy-grid>
|
||||
|
||||
</app-detail-layout>
|
||||
<cnsl-policy-grid [currentPolicy]="currentPolicy" [type]="serviceType" tagForFilter="text"></cnsl-policy-grid>
|
||||
</cnsl-detail-layout>
|
||||
|
@ -18,13 +18,14 @@ import { AdminService } from 'src/app/services/admin.service';
|
||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
import { InfoSectionType } from '../../info-section/info-section.component';
|
||||
import { CnslLinks } from '../../links/links.component';
|
||||
import { GridPolicy, PRIVACY_POLICY } from '../../policy-grid/policies';
|
||||
import { WarnDialogComponent } from '../../warn-dialog/warn-dialog.component';
|
||||
import { PolicyComponentServiceType } from '../policy-component-types.enum';
|
||||
|
||||
@Component({
|
||||
selector: 'app-privacy-policy',
|
||||
selector: 'cnsl-privacy-policy',
|
||||
templateUrl: './privacy-policy.component.html',
|
||||
styleUrls: ['./privacy-policy.component.scss'],
|
||||
})
|
||||
@ -39,6 +40,7 @@ export class PrivacyPolicyComponent implements OnDestroy {
|
||||
public privacyPolicy!: PrivacyPolicy.AsObject;
|
||||
public form!: FormGroup;
|
||||
public currentPolicy: GridPolicy = PRIVACY_POLICY;
|
||||
public InfoSectionType: any = InfoSectionType;
|
||||
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
|
@ -1,33 +1,33 @@
|
||||
<div class="preview" *ngIf="policy">
|
||||
<span class="label">{{label}}</span>
|
||||
<div class="dashed" [ngClass]="{'dark': theme === Theme.DARK, 'light': theme === Theme.LIGHT}" [style.background]="theme == Theme.DARK ? policy.backgroundColorDark : policy.backgroundColor">
|
||||
<div class="login-wrapper" [style.color]="theme == Theme.DARK ? policy.fontColorDark : policy.fontColor">
|
||||
<div class="dashed" [ngClass]="{'dark': theme === Theme.DARK, 'light': theme === Theme.LIGHT}" [style.background]="theme === Theme.DARK ? policy.backgroundColorDark : policy.backgroundColor">
|
||||
<div class="login-wrapper" [style.color]="theme === Theme.DARK ? policy.fontColorDark : policy.fontColor">
|
||||
<ng-container *ngIf="preview === Preview.PREVIEW; else currentimgs">
|
||||
<img *ngIf="images['previewLogo'] && theme == Theme.LIGHT" [src]="images['previewLogo']" alt="logo-mock" />
|
||||
<img *ngIf="images['previewDarkLogo'] && theme == Theme.DARK" [src]="images['previewDarkLogo']" alt="logo-mock" />
|
||||
<img *ngIf="images['previewLogo'] && theme === Theme.LIGHT" [src]="images['previewLogo']" alt="logo-mock" />
|
||||
<img *ngIf="images['previewDarkLogo'] && theme === Theme.DARK" [src]="images['previewDarkLogo']" alt="logo-mock" />
|
||||
</ng-container>
|
||||
|
||||
<ng-template #currentimgs>
|
||||
<img *ngIf="images['logo'] && theme == Theme.LIGHT" [src]="images['logo']" alt="logo-mock" />
|
||||
<img *ngIf="images['darkLogo'] && theme == Theme.DARK" [src]="images['darkLogo']" alt="logo-mock" />
|
||||
<img *ngIf="images['logo'] && theme === Theme.LIGHT" [src]="images['logo']" alt="logo-mock" />
|
||||
<img *ngIf="images['darkLogo'] && theme === Theme.DARK" [src]="images['darkLogo']" alt="logo-mock" />
|
||||
</ng-template>
|
||||
|
||||
<h1 [style.color]="theme == Theme.DARK ? policy.fontColorDark : policy.fontColor">{{'POLICY.PRIVATELABELING.PREVIEW.TITLE' | translate}}</h1>
|
||||
<p [style.color]="theme == Theme.DARK ? policy.fontColorDark : policy.fontColor" class="desc-text">{{'POLICY.PRIVATELABELING.PREVIEW.SECOND' | translate}}</p>
|
||||
<h1 [style.color]="theme === Theme.DARK ? policy.fontColorDark : policy.fontColor">{{'POLICY.PRIVATELABELING.PREVIEW.TITLE' | translate}}</h1>
|
||||
<p [style.color]="theme === Theme.DARK ? policy.fontColorDark : policy.fontColor" class="desc-text">{{'POLICY.PRIVATELABELING.PREVIEW.SECOND' | translate}}</p>
|
||||
<cnsl-form-field class="formfield">
|
||||
<cnsl-label>Loginname</cnsl-label>
|
||||
<input cnslInput [style.color]="theme == Theme.DARK ? policy.fontColorDark : policy.fontColor" value="road.runner"/>
|
||||
<input cnslInput [style.color]="theme === Theme.DARK ? policy.fontColorDark : policy.fontColor" value="road.runner"/>
|
||||
</cnsl-form-field>
|
||||
|
||||
<div class="error-msg" [style.color]="theme == Theme.DARK ? policy.warnColorDark : policy.warnColor">
|
||||
<i class="las la-exclamation-circle" [style.color]="theme == Theme.DARK ? policy.warnColorDark : policy.warnColor"></i>
|
||||
<span [style.color]="theme == Theme.DARK ? policy.warnColorDark : policy.warnColor">{{'POLICY.PRIVATELABELING.PREVIEW.ERROR' | translate}}</span>
|
||||
<div class="error-msg" [style.color]="theme === Theme.DARK ? policy.warnColorDark : policy.warnColor">
|
||||
<i class="las la-exclamation-circle" [style.color]="theme === Theme.DARK ? policy.warnColorDark : policy.warnColor"></i>
|
||||
<span [style.color]="theme === Theme.DARK ? policy.warnColorDark : policy.warnColor">{{'POLICY.PRIVATELABELING.PREVIEW.ERROR' | translate}}</span>
|
||||
</div>
|
||||
|
||||
<div class="btn-wrapper">
|
||||
<button mat-stroked-button [style.color]="theme == Theme.DARK ? policy.primaryColorDark : policy.primaryColor">{{'POLICY.PRIVATELABELING.PREVIEW.SECONDARYBUTTON' | translate}}</button>
|
||||
<button *ngIf="theme == Theme.DARK" mat-raised-button [style.background]="policy.primaryColorDark" [style.color]="policy.primaryColorDark == '#ffffff' ? '#000000' : '#ffffff'">{{'POLICY.PRIVATELABELING.PREVIEW.PRIMARYBUTTON' | translate}}</button>
|
||||
<button *ngIf="theme == Theme.LIGHT" mat-raised-button [style.background]="policy.primaryColor" [style.color]="policy.primaryColor == '#ffffff' ? '#000000' : '#ffffff'">{{'POLICY.PRIVATELABELING.PREVIEW.PRIMARYBUTTON' | translate}}</button>
|
||||
<button mat-stroked-button [style.color]="theme === Theme.DARK ? policy.primaryColorDark : policy.primaryColor">{{'POLICY.PRIVATELABELING.PREVIEW.SECONDARYBUTTON' | translate}}</button>
|
||||
<button *ngIf="theme === Theme.DARK" mat-raised-button [style.background]="policy.primaryColorDark" [style.color]="policy.primaryColorDark === '#ffffff' ? '#000000' : '#ffffff'">{{'POLICY.PRIVATELABELING.PREVIEW.PRIMARYBUTTON' | translate}}</button>
|
||||
<button *ngIf="theme === Theme.LIGHT" mat-raised-button [style.background]="policy.primaryColor" [style.color]="policy.primaryColor === '#ffffff' ? '#000000' : '#ffffff'">{{'POLICY.PRIVATELABELING.PREVIEW.PRIMARYBUTTON' | translate}}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -11,7 +11,7 @@
|
||||
<p class="desc">{{'POLICY.PRIVATELABELING.PREVIEW_DESCRIPTION' | translate}}</p>
|
||||
<cnsl-info-section *ngIf="isDefault"> {{'POLICY.DEFAULTLABEL' | translate}}</cnsl-info-section>
|
||||
|
||||
<cnsl-info-section *ngIf="serviceType == PolicyComponentServiceType.MGMT && (['label_policy.private_label'] | hasFeature | async) == false" [featureLink]="['/org/features']" class="info" type="WARN">
|
||||
<cnsl-info-section *ngIf="serviceType === PolicyComponentServiceType.MGMT && (['label_policy.private_label'] | hasFeature | async) === false" [featureLink]="['/org/features']" class="info" [type]="InfoSectionType.WARN">
|
||||
<span [innerHTML]="'FEATURES.NOTAVAILABLE' | translate: ({value: 'label_policy.private_label'})"></span>
|
||||
</cnsl-info-section>
|
||||
|
||||
@ -34,14 +34,14 @@
|
||||
|
||||
<span class="fill-space"></span>
|
||||
|
||||
<ng-template appHasRole [appHasRole]="['policy.delete']">
|
||||
<ng-template cnslHasRole [hasRole]="['policy.delete']">
|
||||
<button class="reset-button" *ngIf="serviceType === PolicyComponentServiceType.MGMT && !isDefault"
|
||||
matTooltip="{{'POLICY.RESET' | translate}}" color="warn" (click)="removePolicy()" mat-stroked-button>
|
||||
{{'POLICY.RESET' | translate}}
|
||||
</button>
|
||||
</ng-template>
|
||||
|
||||
<button class="activate-button" [disabled]="serviceType == PolicyComponentServiceType.MGMT && (['label_policy.private_label'] | hasFeature | async) == false" mat-raised-button color="primary" (click)="activatePolicy()">
|
||||
<button class="activate-button" [disabled]="serviceType === PolicyComponentServiceType.MGMT && (['label_policy.private_label'] | hasFeature | async) === false" mat-raised-button color="primary" (click)="activatePolicy()">
|
||||
{{'POLICY.PRIVATELABELING.ACTIVATEPREVIEW' | translate}}
|
||||
</button>
|
||||
</div>
|
||||
@ -99,8 +99,8 @@
|
||||
<label class="file-label">
|
||||
<i class="icon las la-image"></i>
|
||||
<span>{{isHoveringOverDarkLogo ? ('POLICY.PRIVATELABELING.RELEASE' | translate): ('POLICY.PRIVATELABELING.DROP' | translate)}}</span>
|
||||
<input #selectedFile style="display: none;" class="file-input" type="file" (change)="onDropLogo(theme, $event.target.files)">
|
||||
<a class="btn" *ngIf="serviceType == PolicyComponentServiceType.ADMIN || (serviceType == PolicyComponentServiceType.MGMT && (['label_policy.private_label'] | hasFeature | async))" (click)="$event.preventDefault(); selectedFile.click();">{{'POLICY.PRIVATELABELING.BTN' | translate}}</a>
|
||||
<input #selectedFile style="display: none;" class="file-input" type="file" (change)="onDropLogo(theme, $any($event.target).files)">
|
||||
<a class="btn" *ngIf="serviceType === PolicyComponentServiceType.ADMIN || (serviceType === PolicyComponentServiceType.MGMT && (['label_policy.private_label'] | hasFeature | async))" (click)="$event.preventDefault(); selectedFile.click();">{{'POLICY.PRIVATELABELING.BTN' | translate}}</a>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
@ -138,8 +138,8 @@
|
||||
<label class="file-label">
|
||||
<i class="icon las la-image"></i>
|
||||
<span>{{isHoveringOverDarkIcon ? ('POLICY.PRIVATELABELING.RELEASE' | translate): ('POLICY.PRIVATELABELING.DROP' | translate)}}</span>
|
||||
<input #selectedFileIcon style="display: none;" class="file-input" type="file" (change)="onDropIcon(theme, $event.target.files)">
|
||||
<a class="btn" *ngIf="serviceType == PolicyComponentServiceType.ADMIN || (serviceType == PolicyComponentServiceType.MGMT && (['label_policy.private_label'] | hasFeature | async))" (click)="$event.preventDefault(); selectedFileIcon.click();">{{'POLICY.PRIVATELABELING.BTN' | translate}}</a>
|
||||
<input #selectedFileIcon style="display: none;" class="file-input" type="file" (change)="onDropIcon(theme, $any($event.target).files)">
|
||||
<a class="btn" *ngIf="serviceType === PolicyComponentServiceType.ADMIN || (serviceType === PolicyComponentServiceType.MGMT && (['label_policy.private_label'] | hasFeature | async))" (click)="$event.preventDefault(); selectedFileIcon.click();">{{'POLICY.PRIVATELABELING.BTN' | translate}}</a>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
@ -155,10 +155,8 @@
|
||||
{{'POLICY.PRIVATELABELING.COLORS' | translate}}</div>
|
||||
</mat-panel-title>
|
||||
</mat-expansion-panel-header>
|
||||
|
||||
<!-- <span class="title">{{ theme === Theme.DARK ? ('POLICY.PRIVATELABELING.DARK' | translate) : ('POLICY.PRIVATELABELING.LIGHT' | translate)}}</span> -->
|
||||
|
||||
<ng-container *ngIf="theme==Theme.DARK">
|
||||
<ng-container *ngIf="theme === Theme.DARK">
|
||||
<div class="colors" *ngIf="data && previewData">
|
||||
<div class="color">
|
||||
<cnsl-color [colorType]="ColorType.BACKGROUNDDARK" (previewChanged)="previewData.backgroundColorDark = $event" name="Background Color" [color]="data.backgroundColorDark" [previewColor]="previewData.backgroundColorDark"></cnsl-color>
|
||||
@ -178,7 +176,7 @@
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="theme==Theme.LIGHT">
|
||||
<ng-container *ngIf="theme === Theme.LIGHT">
|
||||
<div class="colors" *ngIf="data && previewData">
|
||||
<div class="color">
|
||||
<cnsl-color [colorType]="ColorType.BACKGROUNDLIGHT" (previewChanged)="previewData.backgroundColor = $event" name="Background Color" [color]="data.backgroundColor" [previewColor]="previewData.backgroundColor"></cnsl-color>
|
||||
@ -213,12 +211,12 @@
|
||||
</mat-expansion-panel-header>
|
||||
<div class="fonts">
|
||||
<cnsl-info-section class="info-section">{{'POLICY.PRIVATELABELING.FONTINLOGINONLY' | translate}}</cnsl-info-section>
|
||||
<div class="font-preview mat-elevation-z3" *ngIf="preview == Preview.PREVIEW && previewData.fontUrl || preview == Preview.CURRENT && data.fontUrl">
|
||||
<div class="font-preview mat-elevation-z3" *ngIf="preview === Preview.PREVIEW && previewData.fontUrl || preview === Preview.CURRENT && data.fontUrl">
|
||||
<mat-icon class="icon">text_fields</mat-icon>
|
||||
<span>ABC • abc • 123</span>
|
||||
|
||||
<span class="fill-space"></span>
|
||||
<button [disabled]="serviceType == PolicyComponentServiceType.MGMT && (['label_policy.private_label'] | hasFeature | async) == false" matTooltip="{{'ACTIONS.REMOVE' | translate}}" mat-icon-button color="warn" (click)="deleteFont()"><mat-icon>remove_circle</mat-icon></button>
|
||||
<button [disabled]="serviceType === PolicyComponentServiceType.MGMT && (['label_policy.private_label'] | hasFeature | async) === false" matTooltip="{{'ACTIONS.REMOVE' | translate}}" mat-icon-button color="warn" (click)="deleteFont()"><mat-icon>remove_circle</mat-icon></button>
|
||||
</div>
|
||||
|
||||
<div class="dropzone" cnslDropzone (hovered)="toggleHoverFont($event)"
|
||||
@ -227,8 +225,8 @@
|
||||
<label class="file-label">
|
||||
<i class="icon las la-file"></i>
|
||||
<span >{{isHoveringOverFont ? ('POLICY.PRIVATELABELING.RELEASEFONT' | translate): ('POLICY.PRIVATELABELING.DROPFONT' | translate)}}</span>
|
||||
<input #selectedFontFile style="display: none;" class="file-input" type="file" (change)="onDropFont($event.target.files)">
|
||||
<a class="btn" *ngIf="serviceType == PolicyComponentServiceType.ADMIN || (serviceType == PolicyComponentServiceType.MGMT && (['label_policy.private_label'] | hasFeature | async))" (click)="selectedFontFile.click()">{{'POLICY.PRIVATELABELING.BTN' | translate}}</a>
|
||||
<input #selectedFontFile style="display: none;" class="file-input" type="file" (change)="onDropFont($any($event.target).files)">
|
||||
<a class="btn" *ngIf="serviceType === PolicyComponentServiceType.ADMIN || (serviceType === PolicyComponentServiceType.MGMT && (['label_policy.private_label'] | hasFeature | async))" (click)="selectedFontFile.click()">{{'POLICY.PRIVATELABELING.BTN' | translate}}</a>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
@ -246,25 +244,25 @@
|
||||
<div class="adv-container" *ngIf="previewData">
|
||||
|
||||
<ng-container
|
||||
*ngIf="serviceType == PolicyComponentServiceType.MGMT && (['label_policy.private_label'] | hasFeature | async) == false">
|
||||
<cnsl-info-section [featureLink]="['/org/features']" class="info" type="WARN"
|
||||
*ngIf="serviceType === PolicyComponentServiceType.MGMT && (['label_policy.private_label'] | hasFeature | async) === false">
|
||||
<cnsl-info-section [featureLink]="['/org/features']" class="info" [type]="InfoSectionType.WARN"
|
||||
>
|
||||
<span [innerHTML]="'FEATURES.NOTAVAILABLE' | translate: ({value: 'label_policy.private_label'})"></span>
|
||||
</cnsl-info-section>
|
||||
</ng-container>
|
||||
|
||||
<mat-slide-toggle class="toggle" color="primary" ngDefaultControl [disabled]="serviceType == PolicyComponentServiceType.MGMT && (['label_policy.private_label'] | hasFeature | async) == false"
|
||||
<mat-slide-toggle class="toggle" color="primary" ngDefaultControl [disabled]="serviceType === PolicyComponentServiceType.MGMT && (['label_policy.private_label'] | hasFeature | async) === false"
|
||||
[(ngModel)]="previewData.hideLoginNameSuffix" (change)="savePolicy()">
|
||||
{{'POLICY.DATA.HIDELOGINNAMESUFFIX' | translate}}
|
||||
</mat-slide-toggle>
|
||||
|
||||
<ng-container
|
||||
*ngIf="serviceType == PolicyComponentServiceType.MGMT && (['label_policy.watermark'] | hasFeature | async) == false">
|
||||
<cnsl-info-section [featureLink]="['/org/features']" class="info" type="WARN">
|
||||
*ngIf="serviceType === PolicyComponentServiceType.MGMT && (['label_policy.watermark'] | hasFeature | async) === false">
|
||||
<cnsl-info-section [featureLink]="['/org/features']" class="info" type]="InfoSectionType.WARN">
|
||||
<span [innerHTML]="'FEATURES.NOTAVAILABLE' | translate: ({value: 'label_policy.watermark'})"></span>
|
||||
</cnsl-info-section>
|
||||
</ng-container>
|
||||
<mat-slide-toggle class="toggle" color="primary" ngDefaultControl [disabled]="serviceType == PolicyComponentServiceType.MGMT && (['label_policy.watermark'] | hasFeature | async) == false"
|
||||
<mat-slide-toggle class="toggle" color="primary" ngDefaultControl [disabled]="serviceType === PolicyComponentServiceType.MGMT && (['label_policy.watermark'] | hasFeature | async) === false"
|
||||
[(ngModel)]="previewData.disableWatermark" (change)="savePolicy()">
|
||||
{{'POLICY.DATA.DISABLEWATERMARK' | translate}}
|
||||
</mat-slide-toggle>
|
||||
@ -275,8 +273,8 @@
|
||||
<div class="preview-wrapper">
|
||||
<!-- <cnsl-preview class="prev" label="CURRENT CONFIG" [policy]="data"></cnsl-preview> -->
|
||||
<div class="col">
|
||||
<button color="primary" mat-raised-button class="preview-changer" (click)="preview = preview == Preview.PREVIEW ? Preview.CURRENT : Preview.PREVIEW" matTooltip="{{'POLICY.PRIVATELABELING.CHANGEVIEW' | translate}}" [ngClass]="{'active': preview === Preview.PREVIEW}" >
|
||||
<span><span [ngClass]="{'strong': preview == Preview.PREVIEW}">P</span> / <span [ngClass]="{'strong': preview == Preview.CURRENT}">C</span></span>
|
||||
<button color="primary" mat-raised-button class="preview-changer" (click)="preview = preview === Preview.PREVIEW ? Preview.CURRENT : Preview.PREVIEW" matTooltip="{{'POLICY.PRIVATELABELING.CHANGEVIEW' | translate}}" [ngClass]="{'active': preview === Preview.PREVIEW}" >
|
||||
<span><span [ngClass]="{'strong': preview === Preview.PREVIEW}">P</span> / <span [ngClass]="{'strong': preview === Preview.CURRENT}">C</span></span>
|
||||
</button>
|
||||
|
||||
<cnsl-preview *ngIf="preview === Preview.CURRENT" [refresh]="refreshPreview" [images]="images" [preview]="preview" [theme]="theme" class="prev" label="CURRENT" [policy]="data"></cnsl-preview>
|
||||
@ -285,5 +283,5 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<app-policy-grid class="grid" [currentPolicy]="currentPolicy" [type]="serviceType" tagForFilter="text"></app-policy-grid>
|
||||
<cnsl-policy-grid class="grid" [currentPolicy]="currentPolicy" [type]="serviceType" tagForFilter="text"></cnsl-policy-grid>
|
||||
</div>
|
@ -25,6 +25,7 @@ import { StorageKey, StorageLocation, StorageService } from 'src/app/services/st
|
||||
import { ThemeService } from 'src/app/services/theme.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
import { InfoSectionType } from '../../info-section/info-section.component';
|
||||
import { GridPolicy, PRIVATELABEL_POLICY } from '../../policy-grid/policies';
|
||||
import { PolicyComponentServiceType } from '../policy-component-types.enum';
|
||||
|
||||
@ -51,7 +52,7 @@ export enum ColorType {
|
||||
const MAX_ALLOWED_SIZE = 0.5 * 1024 * 1024;
|
||||
|
||||
@Component({
|
||||
selector: 'app-private-labeling-policy',
|
||||
selector: 'cnsl-private-labeling-policy',
|
||||
templateUrl: './private-labeling-policy.component.html',
|
||||
styleUrls: ['./private-labeling-policy.component.scss'],
|
||||
})
|
||||
@ -88,6 +89,7 @@ export class PrivateLabelingPolicyComponent implements OnDestroy {
|
||||
public loadingImages: boolean = false;
|
||||
private org!: Org.AsObject;
|
||||
public currentPolicy: GridPolicy = PRIVATELABEL_POLICY;
|
||||
public InfoSectionType: any = InfoSectionType;
|
||||
constructor(
|
||||
private authService: GrpcAuthService,
|
||||
private route: ActivatedRoute,
|
||||
@ -164,16 +166,18 @@ export class PrivateLabelingPolicyComponent implements OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
public onDropFont(filelist: FileList): Promise<any> | void {
|
||||
const file = filelist.item(0);
|
||||
if (file) {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
return this.handleFontUploadPromise(this.assetService.upload(AssetEndpoint.MGMTFONT, formData, this.org.id));
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
return this.handleFontUploadPromise(this.assetService.upload(AssetEndpoint.IAMFONT, formData, this.org.id));
|
||||
public onDropFont(filelist: FileList | null): Promise<any> | void {
|
||||
if (filelist) {
|
||||
const file = filelist.item(0);
|
||||
if (file) {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
switch (this.serviceType) {
|
||||
case PolicyComponentServiceType.MGMT:
|
||||
return this.handleFontUploadPromise(this.assetService.upload(AssetEndpoint.MGMTFONT, formData, this.org.id));
|
||||
case PolicyComponentServiceType.ADMIN:
|
||||
return this.handleFontUploadPromise(this.assetService.upload(AssetEndpoint.IAMFONT, formData, this.org.id));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,8 +8,8 @@
|
||||
|
||||
<div class="row-lyt">
|
||||
<ng-container *ngFor="let policy of filteredPolicies">
|
||||
<ng-template appHasRole
|
||||
[appHasRole]="type == PolicyComponentServiceType.ADMIN ? policy.iamWithRole : type == PolicyComponentServiceType.MGMT ? policy.orgWithRole : []">
|
||||
<ng-template cnslHasRole
|
||||
[hasRole]="type === PolicyComponentServiceType.ADMIN ? policy.iamWithRole : type === PolicyComponentServiceType.MGMT ? policy.orgWithRole : []">
|
||||
<div class="p-item card">
|
||||
<div class="avatar {{policy.color}}">
|
||||
<mat-icon *ngIf="policy.svgIcon" class="icon" [svgIcon]="policy.svgIcon"></mat-icon>
|
||||
@ -28,7 +28,7 @@
|
||||
</div>
|
||||
<div class="btn-wrapper">
|
||||
<button
|
||||
[routerLink]="type == PolicyComponentServiceType.ADMIN ? policy.iamRouterLink : type == PolicyComponentServiceType.MGMT ? policy.orgRouterLink : null"
|
||||
[routerLink]="type === PolicyComponentServiceType.ADMIN ? policy.iamRouterLink : type === PolicyComponentServiceType.MGMT ? policy.orgRouterLink : null"
|
||||
mat-stroked-button>{{'POLICY.BTN_EDIT' | translate}}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -4,7 +4,7 @@ import { PolicyComponentServiceType, PolicyComponentType } from 'src/app/modules
|
||||
import { GridPolicy, POLICIES } from './policies';
|
||||
|
||||
@Component({
|
||||
selector: 'app-policy-grid',
|
||||
selector: 'cnsl-policy-grid',
|
||||
templateUrl: './policy-grid.component.html',
|
||||
styleUrls: ['./policy-grid.component.scss'],
|
||||
})
|
||||
|
@ -24,13 +24,11 @@ export class ProjectMembersDataSource extends DataSource<Member.AsObject> {
|
||||
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||
public loading$: Observable<boolean> = this.loadingSubject.asObservable();
|
||||
|
||||
constructor(private mgmtService: ManagementService) {
|
||||
constructor(private service: ManagementService) {
|
||||
super();
|
||||
}
|
||||
|
||||
public loadMembers(projectId: string,
|
||||
projectType: ProjectType,
|
||||
pageIndex: number, pageSize: number, grantId?: string): void {
|
||||
public loadMembers(projectId: string, projectType: ProjectType, pageIndex: number, pageSize: number, grantId?: string): void {
|
||||
const offset = pageIndex * pageSize;
|
||||
|
||||
this.loadingSubject.next(true);
|
||||
@ -40,9 +38,9 @@ export class ProjectMembersDataSource extends DataSource<Member.AsObject> {
|
||||
Promise<ListProjectGrantMembersResponse.AsObject>
|
||||
| undefined =
|
||||
projectType === ProjectType.PROJECTTYPE_OWNED ?
|
||||
this.mgmtService.listProjectMembers(projectId, pageSize, offset) :
|
||||
this.service.listProjectMembers(projectId, pageSize, offset) :
|
||||
projectType === ProjectType.PROJECTTYPE_GRANTED && grantId ?
|
||||
this.mgmtService.listProjectGrantMembers(projectId,
|
||||
this.service.listProjectGrantMembers(projectId,
|
||||
grantId, pageSize, offset) : undefined;
|
||||
if (promise) {
|
||||
from(promise).pipe(
|
||||
|
@ -1,29 +1,28 @@
|
||||
<app-detail-layout *ngIf="project"
|
||||
<cnsl-detail-layout *ngIf="project"
|
||||
[backRouterLink]="[ '/projects', (projectType === ProjectType.PROJECTTYPE_OWNED) ? $any(project)?.id : (projectType === ProjectType.PROJECTTYPE_GRANTED) ? $any(project)?.projectId: '']"
|
||||
title="{{projectName}} {{ 'PROJECT.MEMBER.TITLE' | translate }}"
|
||||
description="{{ 'PROJECT.MEMBER.DESCRIPTION' | translate }}">
|
||||
<p class="desc">{{'MEMBER.DOCSINFO' | translate}} <a href="https://docs.zitadel.ch/docs/manuals/admin-managers"
|
||||
target="_blank">ZITADEL Managers</a>.</p>
|
||||
<app-members-table *ngIf="project" [dataSource]="dataSource" [memberRoleOptions]="memberRoleOptions"
|
||||
<cnsl-members-table *ngIf="project" [dataSource]="dataSource" [memberRoleOptions]="memberRoleOptions"
|
||||
(updateRoles)="updateRoles($event.member, $event.change)" [factoryLoadFunc]="changePageFactory"
|
||||
(changedSelection)="selection = $event" [refreshTrigger]="changePage"
|
||||
[canWrite]="['project.member.write$', 'project.member.write:'+ (projectType === ProjectType.PROJECTTYPE_OWNED) ? $any(project)?.id : (projectType === ProjectType.PROJECTTYPE_GRANTED) ? $any(project)?.projectId: ''] | hasRole | async"
|
||||
[canDelete]="['project.member.delete$', 'project.member.delete:'+(projectType === ProjectType.PROJECTTYPE_OWNED) ? $any(project)?.id : (projectType === ProjectType.PROJECTTYPE_GRANTED) ? $any(project)?.projectId: ''] | hasRole | async"
|
||||
(deleteMember)="removeProjectMember($event)">
|
||||
<ng-template appHasRole selectactions
|
||||
[appHasRole]="['project.member.delete:' + (projectType === ProjectType.PROJECTTYPE_OWNED) ? $any(project)?.id : (projectType === ProjectType.PROJECTTYPE_GRANTED) ? $any(project)?.projectId: '', 'project.member.delete']">
|
||||
<ng-template cnslHasRole selectactions
|
||||
[hasRole]="['project.member.delete:' + (projectType === ProjectType.PROJECTTYPE_OWNED) ? $any(project)?.id : (projectType === ProjectType.PROJECTTYPE_GRANTED) ? $any(project)?.projectId: '', 'project.member.delete']">
|
||||
<button (click)="removeProjectMemberSelection()" color="warn"
|
||||
matTooltip="{{'ORG_DETAIL.TABLE.DELETE' | translate}}" class="del-button" mat-raised-button>
|
||||
<i class="las la-trash"></i>
|
||||
{{'ACTIONS.SELECTIONDELETE' | translate}}
|
||||
</button>
|
||||
</ng-template>
|
||||
<ng-template appHasRole writeactions
|
||||
[appHasRole]="['project.member.write:'+(projectType === ProjectType.PROJECTTYPE_OWNED) ? $any(project)?.id : (projectType === ProjectType.PROJECTTYPE_GRANTED) ? $any(project)?.projectId: '','project.member.write']">
|
||||
<ng-template cnslHasRole writeactions
|
||||
[hasRole]="['project.member.write:'+(projectType === ProjectType.PROJECTTYPE_OWNED) ? $any(project)?.id : (projectType === ProjectType.PROJECTTYPE_GRANTED) ? $any(project)?.projectId: '','project.member.write']">
|
||||
<a color="primary" (click)="openAddMember()" color="primary" mat-raised-button>
|
||||
<mat-icon class="icon">add</mat-icon>{{ 'ACTIONS.NEW' | translate }}
|
||||
</a>
|
||||
</ng-template>
|
||||
</app-members-table>
|
||||
</app-detail-layout>
|
||||
<!-- TODO: check for both project.member and project.grant.member permissions -->
|
||||
</cnsl-members-table>
|
||||
</cnsl-detail-layout>
|
||||
|
@ -14,205 +14,209 @@ import { CreationType, MemberCreateDialogComponent } from '../add-member-dialog/
|
||||
import { ProjectMembersDataSource, ProjectType } from './project-members-datasource';
|
||||
|
||||
@Component({
|
||||
selector: 'app-project-members',
|
||||
templateUrl: './project-members.component.html',
|
||||
styleUrls: ['./project-members.component.scss'],
|
||||
selector: 'cnsl-project-members',
|
||||
templateUrl: './project-members.component.html',
|
||||
styleUrls: ['./project-members.component.scss'],
|
||||
})
|
||||
export class ProjectMembersComponent {
|
||||
public INITIALPAGESIZE: number = 25;
|
||||
public project!: Project.AsObject | GrantedProject.AsObject;
|
||||
public projectType: ProjectType = ProjectType.PROJECTTYPE_OWNED;
|
||||
public grantId: string = '';
|
||||
public projectName: string = '';
|
||||
public dataSource!: ProjectMembersDataSource;
|
||||
public memberRoleOptions: string[] = [];
|
||||
public INITIALPAGESIZE: number = 25;
|
||||
public project!: Project.AsObject | GrantedProject.AsObject;
|
||||
public projectType: ProjectType = ProjectType.PROJECTTYPE_OWNED;
|
||||
public grantId: string = '';
|
||||
public projectName: string = '';
|
||||
public dataSource!: ProjectMembersDataSource;
|
||||
public memberRoleOptions: string[] = [];
|
||||
|
||||
public changePageFactory!: Function;
|
||||
public changePage: EventEmitter<void> = new EventEmitter();
|
||||
public selection: Array<Member.AsObject> = [];
|
||||
public changePageFactory!: Function;
|
||||
public changePage: EventEmitter<void> = new EventEmitter();
|
||||
public selection: Array<Member.AsObject> = [];
|
||||
|
||||
public ProjectType: any = ProjectType;
|
||||
constructor(
|
||||
private mgmtService: ManagementService,
|
||||
private dialog: MatDialog,
|
||||
private toast: ToastService,
|
||||
private route: ActivatedRoute) {
|
||||
this.route.data.pipe(take(1)).subscribe(data => {
|
||||
this.projectType = data.type;
|
||||
public ProjectType: any = ProjectType;
|
||||
constructor(
|
||||
private mgmtService: ManagementService,
|
||||
private dialog: MatDialog,
|
||||
private toast: ToastService,
|
||||
private route: ActivatedRoute) {
|
||||
this.route.data.pipe(take(1)).subscribe(data => {
|
||||
this.projectType = data.type;
|
||||
|
||||
this.getRoleOptions();
|
||||
this.getRoleOptions();
|
||||
|
||||
this.route.params.subscribe(params => {
|
||||
this.grantId = params.grantid;
|
||||
if (this.projectType === ProjectType.PROJECTTYPE_OWNED) {
|
||||
this.mgmtService.getProjectByID(params.projectid).then(resp => {
|
||||
if (resp.project) {
|
||||
this.project = resp.project;
|
||||
this.projectName = this.project.name;
|
||||
this.dataSource = new ProjectMembersDataSource(this.mgmtService);
|
||||
this.dataSource.loadMembers(this.project.id, this.projectType, 0, this.INITIALPAGESIZE);
|
||||
this.route.params.subscribe(params => {
|
||||
this.grantId = params.grantid;
|
||||
if (this.projectType === ProjectType.PROJECTTYPE_OWNED) {
|
||||
this.mgmtService.getProjectByID(params.projectid).then(resp => {
|
||||
if (resp.project) {
|
||||
this.project = resp.project;
|
||||
this.projectName = this.project.name;
|
||||
this.dataSource = new ProjectMembersDataSource(this.mgmtService);
|
||||
this.dataSource.loadMembers(this.project.id, this.projectType, 0, this.INITIALPAGESIZE);
|
||||
|
||||
this.changePageFactory = (event?: PageEvent) => {
|
||||
return this.dataSource.loadMembers(
|
||||
(this.project as Project.AsObject).id,
|
||||
this.projectType,
|
||||
event?.pageIndex ?? 0,
|
||||
event?.pageSize ?? this.INITIALPAGESIZE,
|
||||
this.grantId,
|
||||
);
|
||||
};
|
||||
}
|
||||
});
|
||||
} else if (this.projectType === ProjectType.PROJECTTYPE_GRANTED) {
|
||||
this.mgmtService.getGrantedProjectByID(params.projectid, params.grantid).then(resp => {
|
||||
if (resp.grantedProject) {
|
||||
this.project = resp.grantedProject;
|
||||
this.projectName = this.project.projectName;
|
||||
this.dataSource = new ProjectMembersDataSource(this.mgmtService);
|
||||
this.dataSource.loadMembers(this.project.projectId,
|
||||
this.projectType,
|
||||
0,
|
||||
this.INITIALPAGESIZE,
|
||||
this.grantId,
|
||||
);
|
||||
this.changePageFactory = (event?: PageEvent) => {
|
||||
return this.dataSource.loadMembers(
|
||||
(this.project as Project.AsObject).id,
|
||||
this.projectType,
|
||||
event?.pageIndex ?? 0,
|
||||
event?.pageSize ?? this.INITIALPAGESIZE,
|
||||
this.grantId,
|
||||
);
|
||||
};
|
||||
}
|
||||
});
|
||||
} else if (this.projectType === ProjectType.PROJECTTYPE_GRANTED) {
|
||||
this.mgmtService.getGrantedProjectByID(params.projectid, params.grantid).then(resp => {
|
||||
if (resp.grantedProject) {
|
||||
this.project = resp.grantedProject;
|
||||
this.projectName = this.project.projectName;
|
||||
this.dataSource = new ProjectMembersDataSource(this.mgmtService);
|
||||
this.dataSource.loadMembers(this.project.projectId,
|
||||
this.projectType,
|
||||
0,
|
||||
this.INITIALPAGESIZE,
|
||||
this.grantId,
|
||||
);
|
||||
|
||||
this.changePageFactory = (event?: PageEvent) => {
|
||||
return this.dataSource.loadMembers(
|
||||
(this.project as GrantedProject.AsObject).projectId,
|
||||
this.projectType,
|
||||
event?.pageIndex ?? 0,
|
||||
event?.pageSize ?? this.INITIALPAGESIZE,
|
||||
this.grantId,
|
||||
);
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
this.changePageFactory = (event?: PageEvent) => {
|
||||
return this.dataSource.loadMembers(
|
||||
(this.project as GrantedProject.AsObject).projectId,
|
||||
this.projectType,
|
||||
event?.pageIndex ?? 0,
|
||||
event?.pageSize ?? this.INITIALPAGESIZE,
|
||||
this.grantId,
|
||||
);
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public getRoleOptions(): void {
|
||||
if (this.projectType === ProjectType.PROJECTTYPE_GRANTED) {
|
||||
this.mgmtService.listProjectGrantMemberRoles().then(resp => {
|
||||
this.memberRoleOptions = resp.resultList;
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
} else if (this.projectType === ProjectType.PROJECTTYPE_OWNED) {
|
||||
this.mgmtService.listProjectMemberRoles().then(resp => {
|
||||
this.memberRoleOptions = resp.resultList;
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public removeProjectMemberSelection(): void {
|
||||
Promise.all(this.selection.map(member => {
|
||||
if (this.projectType === ProjectType.PROJECTTYPE_OWNED) {
|
||||
return this.mgmtService.removeProjectMember((this.project as Project.AsObject).id, member.userId)
|
||||
.then(() => {
|
||||
this.toast.showInfo('PROJECT.TOAST.MEMBERREMOVED', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
} else if (this.projectType === ProjectType.PROJECTTYPE_GRANTED) {
|
||||
return this.mgmtService.removeProjectGrantMember(
|
||||
(this.project as GrantedProject.AsObject).projectId,
|
||||
this.grantId,
|
||||
member.userId,
|
||||
).then(() => {
|
||||
this.toast.showInfo('PROJECT.TOAST.MEMBERREMOVED', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
} else {
|
||||
return Promise.reject();
|
||||
}
|
||||
})).then(() => {
|
||||
setTimeout(() => {
|
||||
this.changePage.emit();
|
||||
}, 1000);
|
||||
});
|
||||
}
|
||||
|
||||
public removeProjectMember(member: Member.AsObject | Member.AsObject): void {
|
||||
if (this.projectType === ProjectType.PROJECTTYPE_OWNED) {
|
||||
this.mgmtService.removeProjectMember((this.project as Project.AsObject).id, member.userId).then(() => {
|
||||
setTimeout(() => {
|
||||
this.changePage.emit();
|
||||
}, 1000);
|
||||
this.toast.showInfo('PROJECT.TOAST.MEMBERREMOVED', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
} else if (this.projectType === ProjectType.PROJECTTYPE_GRANTED) {
|
||||
this.mgmtService.removeProjectGrantMember((this.project as GrantedProject.AsObject).projectId, this.grantId,
|
||||
member.userId).then(() => {
|
||||
setTimeout(() => {
|
||||
this.changePage.emit();
|
||||
}, 1000);
|
||||
this.toast.showInfo('PROJECT.TOAST.MEMBERREMOVED', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public getRoleOptions(): void {
|
||||
if (this.projectType === ProjectType.PROJECTTYPE_GRANTED) {
|
||||
this.mgmtService.listProjectGrantMemberRoles().then(resp => {
|
||||
this.memberRoleOptions = resp.resultList;
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
} else if (this.projectType === ProjectType.PROJECTTYPE_OWNED) {
|
||||
this.mgmtService.listProjectMemberRoles().then(resp => {
|
||||
this.memberRoleOptions = resp.resultList;
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
public openAddMember(): void {
|
||||
const dialogRef = this.dialog.open(MemberCreateDialogComponent, {
|
||||
data: {
|
||||
creationType: CreationType.PROJECT_OWNED,
|
||||
},
|
||||
width: '400px',
|
||||
});
|
||||
|
||||
public removeProjectMemberSelection(): void {
|
||||
Promise.all(this.selection.map(member => {
|
||||
dialogRef.afterClosed().subscribe(resp => {
|
||||
if (resp) {
|
||||
const users: User.AsObject[] = resp.users;
|
||||
const roles: string[] = resp.roles;
|
||||
|
||||
if (users && users.length && roles && roles.length) {
|
||||
Promise.all(users.map(user => {
|
||||
if (this.projectType === ProjectType.PROJECTTYPE_OWNED) {
|
||||
return this.mgmtService.removeProjectMember((this.project as Project.AsObject).id, member.userId)
|
||||
.then(() => {
|
||||
this.toast.showInfo('PROJECT.TOAST.MEMBERREMOVED', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
return this.mgmtService.addProjectMember((this.project as Project.AsObject).id, user.id, roles);
|
||||
|
||||
} else if (this.projectType === ProjectType.PROJECTTYPE_GRANTED) {
|
||||
return this.mgmtService.removeProjectGrantMember(
|
||||
(this.project as GrantedProject.AsObject).projectId,
|
||||
this.grantId,
|
||||
member.userId,
|
||||
).then(() => {
|
||||
this.toast.showInfo('PROJECT.TOAST.MEMBERREMOVED', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
return this.mgmtService.addProjectGrantMember(
|
||||
(this.project as GrantedProject.AsObject).projectId,
|
||||
this.grantId,
|
||||
user.id,
|
||||
roles,
|
||||
);
|
||||
} else {
|
||||
return Promise.reject();
|
||||
}
|
||||
})).then(() => {
|
||||
})).then(() => {
|
||||
setTimeout(() => {
|
||||
this.changePage.emit();
|
||||
this.changePage.emit();
|
||||
}, 1000);
|
||||
});
|
||||
}
|
||||
|
||||
public removeProjectMember(member: Member.AsObject | Member.AsObject): void {
|
||||
if (this.projectType === ProjectType.PROJECTTYPE_OWNED) {
|
||||
this.mgmtService.removeProjectMember((this.project as Project.AsObject).id, member.userId).then(() => {
|
||||
setTimeout(() => {
|
||||
this.changePage.emit();
|
||||
}, 1000);
|
||||
this.toast.showInfo('PROJECT.TOAST.MEMBERREMOVED', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
} else if (this.projectType === ProjectType.PROJECTTYPE_GRANTED) {
|
||||
this.mgmtService.removeProjectGrantMember((this.project as GrantedProject.AsObject).projectId, this.grantId,
|
||||
member.userId).then(() => {
|
||||
setTimeout(() => {
|
||||
this.changePage.emit();
|
||||
}, 1000);
|
||||
this.toast.showInfo('PROJECT.TOAST.MEMBERREMOVED', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
this.toast.showInfo('PROJECT.TOAST.MEMBERSADDED', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public openAddMember(): void {
|
||||
const dialogRef = this.dialog.open(MemberCreateDialogComponent, {
|
||||
data: {
|
||||
creationType: CreationType.PROJECT_OWNED,
|
||||
},
|
||||
width: '400px',
|
||||
updateRoles(member: Member.AsObject, selectionChange: MatSelectChange): void {
|
||||
if (this.projectType === ProjectType.PROJECTTYPE_OWNED) {
|
||||
this.mgmtService.updateProjectMember((this.project as Project.AsObject).id, member.userId, selectionChange.value)
|
||||
.then(() => {
|
||||
this.toast.showInfo('PROJECT.TOAST.MEMBERCHANGED', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe(resp => {
|
||||
if (resp) {
|
||||
const users: User.AsObject[] = resp.users;
|
||||
const roles: string[] = resp.roles;
|
||||
|
||||
if (users && users.length && roles && roles.length) {
|
||||
Promise.all(users.map(user => {
|
||||
if (this.projectType === ProjectType.PROJECTTYPE_OWNED) {
|
||||
return this.mgmtService.addProjectMember((this.project as Project.AsObject).id, user.id, roles);
|
||||
|
||||
} else if (this.projectType === ProjectType.PROJECTTYPE_GRANTED) {
|
||||
return this.mgmtService.addProjectGrantMember(
|
||||
(this.project as GrantedProject.AsObject).projectId,
|
||||
this.grantId,
|
||||
user.id,
|
||||
roles,
|
||||
);
|
||||
}
|
||||
})).then(() => {
|
||||
setTimeout(() => {
|
||||
this.changePage.emit();
|
||||
}, 1000);
|
||||
this.toast.showInfo('PROJECT.TOAST.MEMBERSADDED', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
} else if (this.projectType === ProjectType.PROJECTTYPE_GRANTED) {
|
||||
this.mgmtService.updateProjectGrantMember((this.project as GrantedProject.AsObject).projectId,
|
||||
this.grantId, member.userId, selectionChange.value)
|
||||
.then(() => {
|
||||
this.toast.showInfo('PROJECT.TOAST.MEMBERCHANGED', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
|
||||
updateRoles(member: Member.AsObject, selectionChange: MatSelectChange): void {
|
||||
if (this.projectType === ProjectType.PROJECTTYPE_OWNED) {
|
||||
this.mgmtService.updateProjectMember((this.project as Project.AsObject).id, member.userId, selectionChange.value)
|
||||
.then(() => {
|
||||
this.toast.showInfo('PROJECT.TOAST.MEMBERCHANGED', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
} else if (this.projectType === ProjectType.PROJECTTYPE_GRANTED) {
|
||||
this.mgmtService.updateProjectGrantMember((this.project as GrantedProject.AsObject).projectId,
|
||||
this.grantId, member.userId, selectionChange.value)
|
||||
.then(() => {
|
||||
this.toast.showInfo('PROJECT.TOAST.MEMBERCHANGED', true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,7 @@
|
||||
{{'ACTIONS.CLOSE' | translate}}
|
||||
</button>
|
||||
|
||||
<button [disabled]="setting == undefined" cdkFocusInitial color="primary" mat-raised-button class="ok-button"
|
||||
<button [disabled]="setting === undefined" cdkFocusInitial color="primary" mat-raised-button class="ok-button"
|
||||
(click)="closeDialog(setting)">
|
||||
{{'ACTIONS.OK' | translate}}
|
||||
</button>
|
||||
|
@ -3,7 +3,7 @@ import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { PrivateLabelingSetting } from 'src/app/proto/generated/zitadel/project_pb';
|
||||
|
||||
@Component({
|
||||
selector: 'app-project-private-labeling-dialog',
|
||||
selector: 'cnsl-project-private-labeling-dialog',
|
||||
templateUrl: './project-private-labeling-dialog.component.html',
|
||||
styleUrls: ['./project-private-labeling-dialog.component.scss'],
|
||||
})
|
||||
|
@ -5,51 +5,51 @@ import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-project-role-detail',
|
||||
templateUrl: './project-role-detail.component.html',
|
||||
styleUrls: ['./project-role-detail.component.scss'],
|
||||
selector: 'cnsl-project-role-detail',
|
||||
templateUrl: './project-role-detail.component.html',
|
||||
styleUrls: ['./project-role-detail.component.scss'],
|
||||
})
|
||||
export class ProjectRoleDetailComponent {
|
||||
public projectId: string = '';
|
||||
public projectId: string = '';
|
||||
|
||||
public formGroup!: FormGroup;
|
||||
constructor(private mgmtService: ManagementService, private toast: ToastService,
|
||||
public dialogRef: MatDialogRef<ProjectRoleDetailComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public data: any) {
|
||||
public formGroup!: FormGroup;
|
||||
constructor(private mgmtService: ManagementService, private toast: ToastService,
|
||||
public dialogRef: MatDialogRef<ProjectRoleDetailComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public data: any) {
|
||||
|
||||
this.projectId = data.projectId;
|
||||
this.formGroup = new FormGroup({
|
||||
key: new FormControl({ value: '', disabled: true }, [Validators.required]),
|
||||
displayName: new FormControl(''),
|
||||
group: new FormControl(''),
|
||||
this.projectId = data.projectId;
|
||||
this.formGroup = new FormGroup({
|
||||
key: new FormControl({ value: '', disabled: true }, [Validators.required]),
|
||||
displayName: new FormControl(''),
|
||||
group: new FormControl(''),
|
||||
});
|
||||
|
||||
this.formGroup.patchValue(data.role);
|
||||
}
|
||||
|
||||
submitForm(): void {
|
||||
if (this.formGroup.valid && this.key?.value && this.group?.value && this.displayName?.value) {
|
||||
this.mgmtService.updateProjectRole(this.projectId, this.key.value, this.displayName.value, this.group.value)
|
||||
.then(() => {
|
||||
this.toast.showInfo('PROJECT.TOAST.ROLECHANGED', true);
|
||||
this.dialogRef.close(true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.formGroup.patchValue(data.role);
|
||||
}
|
||||
public closeDialog(): void {
|
||||
this.dialogRef.close(false);
|
||||
}
|
||||
|
||||
submitForm(): void {
|
||||
if (this.formGroup.valid && this.key?.value && this.group?.value && this.displayName?.value) {
|
||||
this.mgmtService.updateProjectRole(this.projectId, this.key.value, this.displayName.value, this.group.value)
|
||||
.then(() => {
|
||||
this.toast.showInfo('PROJECT.TOAST.ROLECHANGED', true);
|
||||
this.dialogRef.close(true);
|
||||
}).catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public closeDialog(): void {
|
||||
this.dialogRef.close(false);
|
||||
}
|
||||
|
||||
public get key(): AbstractControl | null {
|
||||
return this.formGroup.get('key');
|
||||
}
|
||||
public get displayName(): AbstractControl | null {
|
||||
return this.formGroup.get('displayName');
|
||||
}
|
||||
public get group(): AbstractControl | null {
|
||||
return this.formGroup.get('group');
|
||||
}
|
||||
public get key(): AbstractControl | null {
|
||||
return this.formGroup.get('key');
|
||||
}
|
||||
public get displayName(): AbstractControl | null {
|
||||
return this.formGroup.get('displayName');
|
||||
}
|
||||
public get group(): AbstractControl | null {
|
||||
return this.formGroup.get('group');
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
<app-refresh-table *ngIf="projectId" (refreshed)="refreshPage()" [dataSize]="dataSource?.totalResult"
|
||||
<cnsl-refresh-table *ngIf="projectId" (refreshed)="refreshPage()" [dataSize]="dataSource?.totalResult ?? 0"
|
||||
[emitRefreshOnPreviousRoutes]="['/projects/'+projectId+'/roles/create']" [selection]="selection"
|
||||
[loading]="dataSource?.loading$ | async" [timestamp]="dataSource?.viewTimestamp">
|
||||
|
||||
<ng-template appHasRole [appHasRole]="['project.role.write:' + projectId, 'project.role.write']" actions>
|
||||
<ng-template cnslHasRole [hasRole]="['project.role.write:' + projectId, 'project.role.write']" actions>
|
||||
<a *ngIf="actionsVisible" [disabled]="disabled" [routerLink]="[ '/projects', projectId, 'roles', 'create']"
|
||||
color="primary" mat-raised-button>
|
||||
<mat-icon class="icon">add</mat-icon>{{ 'ACTIONS.NEW' | translate }}
|
||||
@ -55,7 +55,7 @@
|
||||
<th mat-header-cell *matHeaderCellDef></th>
|
||||
<td mat-cell *matCellDef="let role">
|
||||
<button
|
||||
[disabled]="disabled || (['project.role.delete', 'project.role.delete:' + projectId] | hasRole | async) == false"
|
||||
[disabled]="disabled || (['project.role.delete', 'project.role.delete:' + projectId] | hasRole | async) === false"
|
||||
mat-icon-button color="warn" matTooltip="{{'ACTIONS.DELETE' | translate}}"
|
||||
(click)="deleteRole(role)">
|
||||
<i class="las la-trash"></i>
|
||||
@ -67,7 +67,7 @@
|
||||
<tr class="highlight" mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
</table>
|
||||
|
||||
<div *ngIf="(dataSource.loading$ | async) == false && !dataSource?.totalResult" class="no-content-row">
|
||||
<div *ngIf="(dataSource.loading$ | async) === false && !dataSource?.totalResult" class="no-content-row">
|
||||
<i class="las la-exclamation"></i>
|
||||
<span>{{'PROJECT.ROLE.EMPTY' | translate}}</span>
|
||||
</div>
|
||||
@ -76,4 +76,4 @@
|
||||
[pageSizeOptions]="[25, 50, 100, 250]">
|
||||
</cnsl-paginator>
|
||||
</div>
|
||||
</app-refresh-table>
|
||||
</cnsl-refresh-table>
|
@ -13,111 +13,111 @@ import { ProjectRolesDataSource } from './project-roles-datasource';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-project-roles',
|
||||
templateUrl: './project-roles.component.html',
|
||||
styleUrls: ['./project-roles.component.scss'],
|
||||
selector: 'cnsl-project-roles',
|
||||
templateUrl: './project-roles.component.html',
|
||||
styleUrls: ['./project-roles.component.scss'],
|
||||
})
|
||||
export class ProjectRolesComponent implements AfterViewInit, OnInit {
|
||||
@Input() public projectId: string = '';
|
||||
@Input() public disabled: boolean = false;
|
||||
@Input() public actionsVisible: boolean = false;
|
||||
@ViewChild(PaginatorComponent) public paginator!: PaginatorComponent;
|
||||
@ViewChild(MatTable) public table!: MatTable<Role.AsObject>;
|
||||
public dataSource!: ProjectRolesDataSource;
|
||||
public selection: SelectionModel<Role.AsObject> = new SelectionModel<Role.AsObject>(true, []);
|
||||
@Output() public changedSelection: EventEmitter<Array<Role.AsObject>> = new EventEmitter();
|
||||
@Input() public projectId: string = '';
|
||||
@Input() public disabled: boolean = false;
|
||||
@Input() public actionsVisible: boolean = false;
|
||||
@ViewChild(PaginatorComponent) public paginator!: PaginatorComponent;
|
||||
@ViewChild(MatTable) public table!: MatTable<Role.AsObject>;
|
||||
public dataSource!: ProjectRolesDataSource;
|
||||
public selection: SelectionModel<Role.AsObject> = new SelectionModel<Role.AsObject>(true, []);
|
||||
@Output() public changedSelection: EventEmitter<Array<Role.AsObject>> = new EventEmitter();
|
||||
|
||||
/** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */
|
||||
public displayedColumns: string[] = ['select', 'key', 'displayname', 'group', 'creationDate', 'actions'];
|
||||
/** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */
|
||||
public displayedColumns: string[] = ['select', 'key', 'displayname', 'group', 'creationDate', 'actions'];
|
||||
|
||||
constructor(private mgmtService: ManagementService, private toast: ToastService, private dialog: MatDialog) {
|
||||
this.dataSource = new ProjectRolesDataSource(this.mgmtService);
|
||||
}
|
||||
constructor(private mgmtService: ManagementService, private toast: ToastService, private dialog: MatDialog) {
|
||||
this.dataSource = new ProjectRolesDataSource(this.mgmtService);
|
||||
}
|
||||
|
||||
public ngOnInit(): void {
|
||||
this.dataSource.loadRoles(this.projectId, 0, 25, 'asc');
|
||||
public ngOnInit(): void {
|
||||
this.dataSource.loadRoles(this.projectId, 0, 25, 'asc');
|
||||
|
||||
this.selection.changed.subscribe(() => {
|
||||
this.changedSelection.emit(this.selection.selected);
|
||||
});
|
||||
}
|
||||
this.selection.changed.subscribe(() => {
|
||||
this.changedSelection.emit(this.selection.selected);
|
||||
});
|
||||
}
|
||||
|
||||
public ngAfterViewInit(): void {
|
||||
this.paginator.page
|
||||
.pipe(
|
||||
tap(() => this.loadRolesPage()),
|
||||
)
|
||||
.subscribe();
|
||||
}
|
||||
public ngAfterViewInit(): void {
|
||||
this.paginator.page
|
||||
.pipe(
|
||||
tap(() => this.loadRolesPage()),
|
||||
)
|
||||
.subscribe();
|
||||
}
|
||||
|
||||
public selectAllOfGroup(group: string): void {
|
||||
const groupRoles: Role.AsObject[] = this.dataSource.rolesSubject.getValue()
|
||||
.filter(role => role.group === group);
|
||||
this.selection.select(...groupRoles);
|
||||
}
|
||||
public selectAllOfGroup(group: string): void {
|
||||
const groupRoles: Role.AsObject[] = this.dataSource.rolesSubject.getValue()
|
||||
.filter(role => role.group === group);
|
||||
this.selection.select(...groupRoles);
|
||||
}
|
||||
|
||||
private loadRolesPage(): void {
|
||||
this.dataSource.loadRoles(
|
||||
this.projectId,
|
||||
this.paginator.pageIndex,
|
||||
this.paginator.pageSize,
|
||||
);
|
||||
}
|
||||
private loadRolesPage(): void {
|
||||
this.dataSource.loadRoles(
|
||||
this.projectId,
|
||||
this.paginator.pageIndex,
|
||||
this.paginator.pageSize,
|
||||
);
|
||||
}
|
||||
|
||||
public changePage(): void {
|
||||
this.selection.clear();
|
||||
this.loadRolesPage();
|
||||
}
|
||||
public changePage(): void {
|
||||
this.selection.clear();
|
||||
this.loadRolesPage();
|
||||
}
|
||||
|
||||
public isAllSelected(): boolean {
|
||||
const numSelected = this.selection.selected.length;
|
||||
const numRows = this.dataSource.rolesSubject.value.length;
|
||||
return numSelected === numRows;
|
||||
}
|
||||
public isAllSelected(): boolean {
|
||||
const numSelected = this.selection.selected.length;
|
||||
const numRows = this.dataSource.rolesSubject.value.length;
|
||||
return numSelected === numRows;
|
||||
}
|
||||
|
||||
public masterToggle(): void {
|
||||
this.isAllSelected() ?
|
||||
this.selection.clear() :
|
||||
this.dataSource.rolesSubject.value.forEach((row: Role.AsObject) => this.selection.select(row));
|
||||
}
|
||||
public masterToggle(): void {
|
||||
this.isAllSelected() ?
|
||||
this.selection.clear() :
|
||||
this.dataSource.rolesSubject.value.forEach((row: Role.AsObject) => this.selection.select(row));
|
||||
}
|
||||
|
||||
public deleteRole(role: Role.AsObject): Promise<any> {
|
||||
const index = this.dataSource.rolesSubject.value.findIndex(iter => iter.key === role.key);
|
||||
public deleteRole(role: Role.AsObject): Promise<any> {
|
||||
const index = this.dataSource.rolesSubject.value.findIndex(iter => iter.key === role.key);
|
||||
|
||||
return this.mgmtService.removeProjectRole(this.projectId, role.key).then(() => {
|
||||
this.toast.showInfo('PROJECT.TOAST.ROLEREMOVED', true);
|
||||
return this.mgmtService.removeProjectRole(this.projectId, role.key).then(() => {
|
||||
this.toast.showInfo('PROJECT.TOAST.ROLEREMOVED', true);
|
||||
|
||||
if (index > -1) {
|
||||
this.dataSource.rolesSubject.value.splice(index, 1);
|
||||
this.dataSource.rolesSubject.next(this.dataSource.rolesSubject.value);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (index > -1) {
|
||||
this.dataSource.rolesSubject.value.splice(index, 1);
|
||||
this.dataSource.rolesSubject.next(this.dataSource.rolesSubject.value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public removeRole(role: Role.AsObject, index: number): void {
|
||||
this.mgmtService
|
||||
.removeProjectRole(this.projectId, role.key)
|
||||
.then(() => {
|
||||
this.toast.showInfo('PROJECT.TOAST.ROLEREMOVED', true);
|
||||
this.dataSource.rolesSubject.value.splice(index, 1);
|
||||
this.dataSource.rolesSubject.next(this.dataSource.rolesSubject.value);
|
||||
})
|
||||
.catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
public removeRole(role: Role.AsObject, index: number): void {
|
||||
this.mgmtService
|
||||
.removeProjectRole(this.projectId, role.key)
|
||||
.then(() => {
|
||||
this.toast.showInfo('PROJECT.TOAST.ROLEREMOVED', true);
|
||||
this.dataSource.rolesSubject.value.splice(index, 1);
|
||||
this.dataSource.rolesSubject.next(this.dataSource.rolesSubject.value);
|
||||
})
|
||||
.catch(error => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
|
||||
public openDetailDialog(role: Role.AsObject): void {
|
||||
this.dialog.open(ProjectRoleDetailComponent, {
|
||||
data: {
|
||||
role,
|
||||
projectId: this.projectId,
|
||||
},
|
||||
width: '400px',
|
||||
});
|
||||
}
|
||||
public openDetailDialog(role: Role.AsObject): void {
|
||||
this.dialog.open(ProjectRoleDetailComponent, {
|
||||
data: {
|
||||
role,
|
||||
projectId: this.projectId,
|
||||
},
|
||||
width: '400px',
|
||||
});
|
||||
}
|
||||
|
||||
public refreshPage(): void {
|
||||
this.dataSource.loadRoles(this.projectId, this.paginator.pageIndex, this.paginator.pageSize);
|
||||
}
|
||||
public refreshPage(): void {
|
||||
this.dataSource.loadRoles(this.projectId, this.paginator.pageIndex, this.paginator.pageSize);
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ const rotate = animation([
|
||||
),
|
||||
]);
|
||||
@Component({
|
||||
selector: 'app-refresh-table',
|
||||
selector: 'cnsl-refresh-table',
|
||||
templateUrl: './refresh-table.component.html',
|
||||
styleUrls: ['./refresh-table.component.scss'],
|
||||
animations: [
|
||||
@ -29,10 +29,10 @@ const rotate = animation([
|
||||
})
|
||||
export class RefreshTableComponent implements OnInit {
|
||||
@Input() public selection: SelectionModel<any> = new SelectionModel<any>(true, []);
|
||||
@Input() public timestamp!: Timestamp.AsObject;
|
||||
@Input() public timestamp: Timestamp.AsObject | undefined = undefined;
|
||||
@Input() public dataSize: number = 0;
|
||||
@Input() public emitRefreshAfterTimeoutInMs: number = 0;
|
||||
@Input() public loading: boolean = false;
|
||||
@Input() public loading: boolean | null = false;
|
||||
@Input() public emitRefreshOnPreviousRoutes: string[] = [];
|
||||
@Output() public refreshed: EventEmitter<void> = new EventEmitter();
|
||||
@Input() public hideRefresh: boolean = false;
|
||||
|
@ -17,7 +17,7 @@ export enum ProjectAutocompleteType {
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-search-project-autocomplete',
|
||||
selector: 'cnsl-search-project-autocomplete',
|
||||
templateUrl: './search-project-autocomplete.component.html',
|
||||
styleUrls: ['./search-project-autocomplete.component.scss'],
|
||||
})
|
||||
@ -95,9 +95,9 @@ export class SearchProjectAutocompleteComponent implements OnDestroy {
|
||||
this.unsubscribed$.next();
|
||||
}
|
||||
|
||||
public displayFn(project?: any): string | undefined {
|
||||
public displayFn(project?: any): string {
|
||||
return (project && project.projectName) ? `${project.projectName}` :
|
||||
(project && project.name) ? `${project.name}` : undefined;
|
||||
(project && project.name) ? `${project.name}` : '';
|
||||
}
|
||||
|
||||
public add(event: MatChipInputEvent): void {
|
||||
@ -111,6 +111,8 @@ export class SearchProjectAutocompleteComponent implements OnDestroy {
|
||||
return project.projectName === value;
|
||||
} else if (project?.name) {
|
||||
return project.name === value;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
if (index > -1) {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user