mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-11 21:37:32 +00:00
feat(console): v2 feature cleanup, fix styling issues, instance naming (#3530)
* new console * move npm ci to angular build * rel path for assets * local grpc copy * login policy, rm clear views, features rel path * login_hint param * edit default color values * remove features, zitadel-tier, contact styles * html formatter * rm feature restrictions, fix theming issues * instance naming * statehandler * rm class * rm class * update footer links * statehandler * stroked color Co-authored-by: Livio Amstutz <livio.a@gmail.com>
This commit is contained in:
@@ -145,7 +145,7 @@ SystemDefaults:
|
||||
ApplicationKeySize: 2048
|
||||
Multifactors:
|
||||
OTP:
|
||||
Issuer: 'ZITADEL'
|
||||
Issuer: "ZITADEL"
|
||||
DomainVerification:
|
||||
VerificationGenerator:
|
||||
Length: 32
|
||||
@@ -154,7 +154,7 @@ SystemDefaults:
|
||||
IncludeDigits: true
|
||||
IncludeSymbols: false
|
||||
Notifications:
|
||||
FileSystemPath: '.notifications/'
|
||||
FileSystemPath: ".notifications/"
|
||||
KeyConfig:
|
||||
Size: 2048
|
||||
PrivateKeyLifetime: 6h
|
||||
@@ -189,35 +189,35 @@ DefaultInstance:
|
||||
IncludeSymbols: false
|
||||
InitializeUserCode:
|
||||
Length: 6
|
||||
Expiry: '72h'
|
||||
Expiry: "72h"
|
||||
IncludeLowerLetters: false
|
||||
IncludeUpperLetters: true
|
||||
IncludeDigits: true
|
||||
IncludeSymbols: false
|
||||
EmailVerificationCode:
|
||||
Length: 6
|
||||
Expiry: '1h'
|
||||
Expiry: "1h"
|
||||
IncludeLowerLetters: false
|
||||
IncludeUpperLetters: true
|
||||
IncludeDigits: true
|
||||
IncludeSymbols: false
|
||||
PhoneVerificationCode:
|
||||
Length: 6
|
||||
Expiry: '1h'
|
||||
Expiry: "1h"
|
||||
IncludeLowerLetters: false
|
||||
IncludeUpperLetters: true
|
||||
IncludeDigits: true
|
||||
IncludeSymbols: false
|
||||
PasswordVerificationCode:
|
||||
Length: 6
|
||||
Expiry: '1h'
|
||||
Expiry: "1h"
|
||||
IncludeLowerLetters: false
|
||||
IncludeUpperLetters: true
|
||||
IncludeDigits: true
|
||||
IncludeSymbols: false
|
||||
PasswordlessInitCode:
|
||||
Length: 12
|
||||
Expiry: '1h'
|
||||
Expiry: "1h"
|
||||
IncludeLowerLetters: true
|
||||
IncludeUpperLetters: true
|
||||
IncludeDigits: true
|
||||
@@ -278,16 +278,16 @@ DefaultInstance:
|
||||
PrivacyPolicy:
|
||||
TOSLink: https://docs.zitadel.ch/docs/legal/terms-of-service
|
||||
PrivacyLink: https://docs.zitadel.ch/docs/legal/privacy-policy
|
||||
HelpLink: ''
|
||||
HelpLink: ""
|
||||
LabelPolicy:
|
||||
PrimaryColor: '#5469d4'
|
||||
BackgroundColor: '#fafafa'
|
||||
WarnColor: '#f44336'
|
||||
FontColor: '#000000'
|
||||
PrimaryColorDark: '#5469d4'
|
||||
BackgroundColorDark: '#212121'
|
||||
WarnColorDark: '#f44336'
|
||||
FontColorDark: '#ffffff'
|
||||
PrimaryColor: "#5469d4"
|
||||
BackgroundColor: "#fafafa"
|
||||
WarnColor: "#cd3d56"
|
||||
FontColor: "#000000"
|
||||
PrimaryColorDark: "#bbbafa"
|
||||
BackgroundColorDark: "#111827"
|
||||
WarnColorDark: "#ff3b5b"
|
||||
FontColorDark: "#ffffff"
|
||||
HideLoginNameSuffix: false
|
||||
ErrorMsgPopup: false
|
||||
DisableWatermark: false
|
||||
@@ -379,7 +379,7 @@ DefaultInstance:
|
||||
|
||||
InternalAuthZ:
|
||||
RolePermissionMappings:
|
||||
- Role: 'IAM_OWNER'
|
||||
- Role: "IAM_OWNER"
|
||||
Permissions:
|
||||
- "iam.read"
|
||||
- "iam.write"
|
||||
@@ -448,7 +448,7 @@ InternalAuthZ:
|
||||
- "project.grant.member.read"
|
||||
- "project.grant.member.write"
|
||||
- "project.grant.member.delete"
|
||||
- Role: 'IAM_OWNER_VIEWER'
|
||||
- Role: "IAM_OWNER_VIEWER"
|
||||
Permissions:
|
||||
- "iam.read"
|
||||
- "iam.features.read"
|
||||
@@ -474,7 +474,7 @@ InternalAuthZ:
|
||||
- "project.app.read"
|
||||
- "project.grant.read"
|
||||
- "project.grant.member.read"
|
||||
- Role: 'IAM_ORG_MANAGER'
|
||||
- Role: "IAM_ORG_MANAGER"
|
||||
Permissions:
|
||||
- "org.read"
|
||||
- "org.global.read"
|
||||
@@ -524,7 +524,7 @@ InternalAuthZ:
|
||||
- "project.grant.member.read"
|
||||
- "project.grant.member.write"
|
||||
- "project.grant.member.delete"
|
||||
- Role: 'IAM_USER_MANAGER'
|
||||
- Role: "IAM_USER_MANAGER"
|
||||
Permissions:
|
||||
- "org.read"
|
||||
- "org.global.read"
|
||||
@@ -547,7 +547,7 @@ InternalAuthZ:
|
||||
- "project.grant.write"
|
||||
- "project.grant.delete"
|
||||
- "project.grant.member.read"
|
||||
- Role: 'ORG_OWNER'
|
||||
- Role: "ORG_OWNER"
|
||||
Permissions:
|
||||
- "org.read"
|
||||
- "org.global.read"
|
||||
@@ -596,7 +596,7 @@ InternalAuthZ:
|
||||
- "project.grant.member.read"
|
||||
- "project.grant.member.write"
|
||||
- "project.grant.member.delete"
|
||||
- Role: 'ORG_USER_MANAGER'
|
||||
- Role: "ORG_USER_MANAGER"
|
||||
Permissions:
|
||||
- "user.read"
|
||||
- "user.global.read"
|
||||
@@ -608,7 +608,7 @@ InternalAuthZ:
|
||||
- "user.membership.read"
|
||||
- "project.read"
|
||||
- "project.role.read"
|
||||
- Role: 'ORG_OWNER_VIEWER'
|
||||
- Role: "ORG_OWNER_VIEWER"
|
||||
Permissions:
|
||||
- "org.read"
|
||||
- "org.member.read"
|
||||
@@ -628,7 +628,7 @@ InternalAuthZ:
|
||||
- "project.grant.read"
|
||||
- "project.grant.member.read"
|
||||
- "project.grant.user.grant.read"
|
||||
- Role: 'ORG_USER_PERMISSION_EDITOR'
|
||||
- Role: "ORG_USER_PERMISSION_EDITOR"
|
||||
Permissions:
|
||||
- "org.read"
|
||||
- "org.member.read"
|
||||
@@ -644,7 +644,7 @@ InternalAuthZ:
|
||||
- "project.app.read"
|
||||
- "project.grant.read"
|
||||
- "project.grant.member.read"
|
||||
- Role: 'ORG_PROJECT_PERMISSION_EDITOR'
|
||||
- Role: "ORG_PROJECT_PERMISSION_EDITOR"
|
||||
Permissions:
|
||||
- "org.read"
|
||||
- "org.member.read"
|
||||
@@ -662,13 +662,13 @@ InternalAuthZ:
|
||||
- "project.grant.write"
|
||||
- "project.grant.delete"
|
||||
- "project.grant.member.read"
|
||||
- Role: 'ORG_PROJECT_CREATOR'
|
||||
- Role: "ORG_PROJECT_CREATOR"
|
||||
Permissions:
|
||||
- "user.global.read"
|
||||
- "policy.read"
|
||||
- "project.read:self"
|
||||
- "project.create"
|
||||
- Role: 'PROJECT_OWNER'
|
||||
- Role: "PROJECT_OWNER"
|
||||
Permissions:
|
||||
- "org.global.read"
|
||||
- "policy.read"
|
||||
@@ -696,7 +696,7 @@ InternalAuthZ:
|
||||
- "user.grant.write"
|
||||
- "user.grant.delete"
|
||||
- "user.membership.read"
|
||||
- Role: 'PROJECT_OWNER_VIEWER'
|
||||
- Role: "PROJECT_OWNER_VIEWER"
|
||||
Permissions:
|
||||
- "policy.read"
|
||||
- "project.read"
|
||||
@@ -709,12 +709,12 @@ InternalAuthZ:
|
||||
- "user.global.read"
|
||||
- "user.grant.read"
|
||||
- "user.membership.read"
|
||||
- Role: 'SELF_MANAGEMENT_GLOBAL'
|
||||
- Role: "SELF_MANAGEMENT_GLOBAL"
|
||||
Permissions:
|
||||
- "org.create"
|
||||
- "policy.read"
|
||||
- "user.self.delete"
|
||||
- Role: 'PROJECT_OWNER_GLOBAL'
|
||||
- Role: "PROJECT_OWNER_GLOBAL"
|
||||
Permissions:
|
||||
- "org.global.read"
|
||||
- "policy.read"
|
||||
@@ -735,7 +735,7 @@ InternalAuthZ:
|
||||
- "user.grant.write"
|
||||
- "user.grant.delete"
|
||||
- "user.membership.read"
|
||||
- Role: 'PROJECT_OWNER_VIEWER_GLOBAL'
|
||||
- Role: "PROJECT_OWNER_VIEWER_GLOBAL"
|
||||
Permissions:
|
||||
- "policy.read"
|
||||
- "project.read"
|
||||
@@ -747,7 +747,7 @@ InternalAuthZ:
|
||||
- "user.global.read"
|
||||
- "user.grant.read"
|
||||
- "user.membership.read"
|
||||
- Role: 'PROJECT_GRANT_OWNER'
|
||||
- Role: "PROJECT_GRANT_OWNER"
|
||||
Permissions:
|
||||
- "policy.read"
|
||||
- "org.global.read"
|
||||
@@ -762,7 +762,7 @@ InternalAuthZ:
|
||||
- "user.grant.write"
|
||||
- "user.grant.delete"
|
||||
- "user.membership.read"
|
||||
- Role: 'PROJECT_GRANT_OWNER_VIEWER'
|
||||
- Role: "PROJECT_GRANT_OWNER_VIEWER"
|
||||
Permissions:
|
||||
- "policy.read"
|
||||
- "project.read"
|
||||
|
11326
console/package-lock.json
generated
11326
console/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,11 +1,25 @@
|
||||
<ng-container *ngIf="(authService.user | async) || undefined as user">
|
||||
<ng-container *ngIf="((['iam.read$','iam.write$'] | hasRole)) as iamuser$">
|
||||
<ng-container *ngIf="['iam.read$', 'iam.write$'] | hasRole as iamuser$">
|
||||
<div class="main-container">
|
||||
<cnsl-header *ngIf="user" [org]="org" [user]="user" [isDarkTheme]="componentCssClass === 'dark-theme'"
|
||||
[labelpolicy]="labelpolicy" (changedActiveOrg)="changedOrg($event)"></cnsl-header>
|
||||
<cnsl-header
|
||||
*ngIf="user"
|
||||
[org]="org"
|
||||
[user]="user"
|
||||
[isDarkTheme]="componentCssClass === 'dark-theme'"
|
||||
[labelpolicy]="labelpolicy"
|
||||
(changedActiveOrg)="changedOrg($event)"
|
||||
></cnsl-header>
|
||||
|
||||
<cnsl-nav id="mainnav" class="nav" [ngClass]="{ 'shadow': yoffset > 60}" *ngIf="user" [org]="org" [user]="user"
|
||||
[isDarkTheme]="componentCssClass === 'dark-theme'" [labelpolicy]="labelpolicy"></cnsl-nav>
|
||||
<cnsl-nav
|
||||
id="mainnav"
|
||||
class="nav"
|
||||
[ngClass]="{ shadow: yoffset > 60 }"
|
||||
*ngIf="user"
|
||||
[org]="org"
|
||||
[user]="user"
|
||||
[isDarkTheme]="componentCssClass === 'dark-theme'"
|
||||
[labelpolicy]="labelpolicy"
|
||||
></cnsl-nav>
|
||||
<div class="router-container" [@routeAnimations]="prepareRoute(outlet)">
|
||||
<div class="outlet">
|
||||
<router-outlet class="outlet" #outlet="outlet"></router-outlet>
|
||||
@@ -15,4 +29,4 @@
|
||||
<cnsl-footer [privateLabelPolicy]="labelpolicy"></cnsl-footer>
|
||||
</div>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
|
@@ -341,6 +341,7 @@ export class AppComponent implements OnInit, OnDestroy {
|
||||
|
||||
public onSetTheme(theme: string): void {
|
||||
localStorage.setItem('theme', theme);
|
||||
this.overlayContainer.getContainerElement().classList.remove(theme === 'dark-theme' ? 'light-theme' : 'dark-theme');
|
||||
this.overlayContainer.getContainerElement().classList.add(theme);
|
||||
this.componentCssClass = theme;
|
||||
}
|
||||
|
@@ -17,7 +17,6 @@ import { QuicklinkModule } from 'ngx-quicklink';
|
||||
import { from, Observable } from 'rxjs';
|
||||
import { InfoOverlayModule } from 'src/app/modules/info-overlay/info-overlay.module';
|
||||
import { AssetService } from 'src/app/services/asset.service';
|
||||
import { SubscriptionService } from 'src/app/services/subscription.service';
|
||||
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
import { AppComponent } from './app.component';
|
||||
@@ -45,8 +44,8 @@ import { OverlayService } from './services/overlay/overlay.service';
|
||||
import { RefreshService } from './services/refresh.service';
|
||||
import { SeoService } from './services/seo.service';
|
||||
import {
|
||||
StatehandlerProcessorService,
|
||||
StatehandlerProcessorServiceImpl,
|
||||
StatehandlerProcessorService,
|
||||
StatehandlerProcessorServiceImpl,
|
||||
} from './services/statehandler/statehandler-processor.service';
|
||||
import { StatehandlerService, StatehandlerServiceImpl } from './services/statehandler/statehandler.service';
|
||||
import { StorageService } from './services/storage.service';
|
||||
@@ -174,7 +173,6 @@ const authConfig: AuthConfig = {
|
||||
GrpcAuthService,
|
||||
ManagementService,
|
||||
AdminService,
|
||||
SubscriptionService,
|
||||
KeyboardShortcutsService,
|
||||
AssetService,
|
||||
NavigationService,
|
||||
|
@@ -1,33 +0,0 @@
|
||||
import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';
|
||||
import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
|
||||
|
||||
@Directive({
|
||||
selector: '[cnslHasFeature]',
|
||||
})
|
||||
export class HasFeatureDirective {
|
||||
private hasView: boolean = false;
|
||||
@Input() public set hasFeature(features: string[] | RegExp[] | undefined) {
|
||||
if (features && features.length > 0) {
|
||||
this.authService.canUseFeature(features).subscribe((isAllowed) => {
|
||||
if (isAllowed && !this.hasView) {
|
||||
this.viewContainerRef.clear();
|
||||
this.viewContainerRef.createEmbeddedView(this.templateRef);
|
||||
} else {
|
||||
this.viewContainerRef.clear();
|
||||
this.hasView = false;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (!this.hasView) {
|
||||
this.viewContainerRef.clear();
|
||||
this.viewContainerRef.createEmbeddedView(this.templateRef);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constructor(
|
||||
private authService: GrpcAuthService,
|
||||
protected templateRef: TemplateRef<any>,
|
||||
protected viewContainerRef: ViewContainerRef,
|
||||
) {}
|
||||
}
|
@@ -1,19 +0,0 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
|
||||
import { HasFeatureDirective } from './has-feature.directive';
|
||||
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
HasFeatureDirective,
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
],
|
||||
exports: [
|
||||
HasFeatureDirective,
|
||||
],
|
||||
})
|
||||
export class HasFeatureModule { }
|
@@ -1,24 +1,36 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot } from '@angular/router';
|
||||
import { AuthConfig } from 'angular-oauth2-oidc';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
import { AuthenticationService } from '../services/authentication.service';
|
||||
import { GrpcAuthService } from '../services/grpc-auth.service';
|
||||
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class AuthGuard implements CanActivate {
|
||||
constructor(private auth: AuthenticationService, private authService: GrpcAuthService) { }
|
||||
constructor(private auth: AuthenticationService, private authService: GrpcAuthService) {}
|
||||
|
||||
public canActivate(
|
||||
_: ActivatedRouteSnapshot,
|
||||
state: RouterStateSnapshot,
|
||||
): Observable<boolean> | Promise<boolean> | Promise<any> | boolean {
|
||||
if (!this.auth.authenticated) {
|
||||
return this.auth.authenticate();
|
||||
}
|
||||
return this.auth.authenticated;
|
||||
public canActivate(
|
||||
route: ActivatedRouteSnapshot,
|
||||
state: RouterStateSnapshot,
|
||||
): Observable<boolean> | Promise<boolean> | Promise<any> | boolean {
|
||||
if (!this.auth.authenticated) {
|
||||
if (route.queryParams && route.queryParams['login_hint']) {
|
||||
const hint = route.queryParams['login_hint'];
|
||||
const configWithPrompt: Partial<AuthConfig> = {
|
||||
customQueryParams: {
|
||||
// prompt: 'select_account',
|
||||
} as any,
|
||||
};
|
||||
(configWithPrompt as any).customQueryParams['login_hint'] = hint;
|
||||
console.log('auth', hint);
|
||||
this.auth.authenticate(configWithPrompt);
|
||||
} else {
|
||||
return this.auth.authenticate();
|
||||
}
|
||||
}
|
||||
return this.auth.authenticated;
|
||||
}
|
||||
}
|
||||
|
@@ -1,39 +1,54 @@
|
||||
<div class="accounts-card" cnslOutsideClick (clickOutside)="closeCard($event)">
|
||||
<cnsl-avatar (click)="editUserProfile()" *ngIf="user.human?.profile && user.human?.profile?.displayName"
|
||||
class="avatar" [ngClass]="{'iam-user': iamuser}" [forColor]="user.preferredLoginName"
|
||||
<cnsl-avatar
|
||||
(click)="editUserProfile()"
|
||||
*ngIf="user.human?.profile && user.human?.profile?.displayName"
|
||||
class="avatar"
|
||||
[ngClass]="{ 'iam-user': iamuser }"
|
||||
[forColor]="user.preferredLoginName"
|
||||
[avatarUrl]="user.human?.profile?.avatarUrl || ''"
|
||||
[name]="(user.human && user.human.profile && user.human.profile?.displayName) ? user.human.profile.displayName : (user.human?.profile?.firstName + ' '+ user.human?.profile?.lastName)"
|
||||
[size]="80">
|
||||
[name]="
|
||||
user.human && user.human.profile && user.human.profile?.displayName
|
||||
? user.human.profile.displayName
|
||||
: user.human?.profile?.firstName + ' ' + user.human?.profile?.lastName
|
||||
"
|
||||
[size]="80"
|
||||
>
|
||||
</cnsl-avatar>
|
||||
|
||||
<span class="u-name">{{user.human?.profile?.displayName ? user.human?.profile?.displayName : 'A'}}</span>
|
||||
<span class="u-email" *ngIf="user.preferredLoginName">{{user.preferredLoginName}}</span>
|
||||
<a [routerLink]="[ '/system']" class="iamuser" *ngIf="iamuser" (click)="close()">
|
||||
<span class="label">{{'MENU.SYSTEM' | translate}}</span>
|
||||
<span class="u-name">{{ user.human?.profile?.displayName ? user.human?.profile?.displayName : 'A' }}</span>
|
||||
<span class="u-email" *ngIf="user.preferredLoginName">{{ user.preferredLoginName }}</span>
|
||||
<a [routerLink]="['/system']" class="iamuser" *ngIf="iamuser" (click)="close()">
|
||||
<span class="label">{{ 'MENU.INSTANCE' | translate }}</span>
|
||||
<a class="iambtn">
|
||||
<i class="las la-cog"></i>
|
||||
</a>
|
||||
</a>
|
||||
|
||||
<a [routerLink]="[ '/org']" class="iamuser" *ngIf="isOnSystem" (click)="close()">
|
||||
<span class="label">{{'MENU.ORGANIZATION' | translate}}</span>
|
||||
<a [routerLink]="['/org']" class="iamuser" *ngIf="isOnSystem" (click)="close()">
|
||||
<span class="label">{{ 'MENU.ORGANIZATION' | translate }}</span>
|
||||
<a class="iambtn">
|
||||
<i class="las la-cog"></i>
|
||||
</a>
|
||||
</a>
|
||||
|
||||
<button (click)="editUserProfile()" mat-stroked-button>{{'USER.EDITACCOUNT' | translate}}</button>
|
||||
<button (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)">
|
||||
<cnsl-avatar *ngIf="session && session.displayName" class="small-avatar" [avatarUrl]="session.avatarUrl || ''"
|
||||
[name]="session.displayName" [forColor]="session.loginName" [size]="32">
|
||||
<cnsl-avatar
|
||||
*ngIf="session && session.displayName"
|
||||
class="small-avatar"
|
||||
[avatarUrl]="session.avatarUrl || ''"
|
||||
[name]="session.displayName"
|
||||
[forColor]="session.loginName"
|
||||
[size]="32"
|
||||
>
|
||||
</cnsl-avatar>
|
||||
|
||||
<div class="col">
|
||||
<span class="user-title">{{session.displayName ? session.displayName : session.userName}} </span>
|
||||
<span class="loginname">{{session.loginName}}</span>
|
||||
<span class="email">{{'USER.STATE.'+session.authState | translate}}</span>
|
||||
<span class="user-title">{{ session.displayName ? session.displayName : session.userName }} </span>
|
||||
<span class="loginname">{{ session.loginName }}</span>
|
||||
<span class="email">{{ 'USER.STATE.' + session.authState | translate }}</span>
|
||||
</div>
|
||||
<span class="fill-space"></span>
|
||||
<mat-icon>keyboard_arrow_right</mat-icon>
|
||||
@@ -43,12 +58,12 @@
|
||||
<i class="las la-user-plus"></i>
|
||||
</div>
|
||||
<span class="col">
|
||||
<span class="user-title">{{'USER.ADDACCOUNT' | translate}}</span>
|
||||
<span class="user-title">{{ 'USER.ADDACCOUNT' | translate }}</span>
|
||||
</span>
|
||||
<span class="fill-space"></span>
|
||||
<mat-icon>keyboard_arrow_right</mat-icon>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<button (click)="logout()" color="warn" mat-stroked-button>{{'MENU.LOGOUT' | translate}}</button>
|
||||
</div>
|
||||
<button (click)="logout()" color="warn" mat-stroked-button>{{ 'MENU.LOGOUT' | translate }}</button>
|
||||
</div>
|
||||
|
@@ -1,21 +1,26 @@
|
||||
<span class="title" mat-dialog-title>{{'MEMBER.EDITROLE' | translate}}</span>
|
||||
<span class="title" mat-dialog-title>{{ 'MEMBER.EDITROLE' | translate }}</span>
|
||||
<div mat-dialog-content>
|
||||
<p class="desc"> {{'MEMBER.EDITFOR' | translate: ({value: data.user})}}</p>
|
||||
<p class="desc">{{ 'MEMBER.EDITFOR' | translate: { value: data.user } }}</p>
|
||||
<div class="roles-selection">
|
||||
<mat-checkbox class="role-cb" *ngFor="let role of allRoles" color="primary" (change)="toggleRole(role)"
|
||||
[checked]="selectedRoles.includes(role)">
|
||||
<mat-checkbox
|
||||
class="role-cb"
|
||||
*ngFor="let role of allRoles"
|
||||
color="primary"
|
||||
(change)="toggleRole(role)"
|
||||
[checked]="selectedRoles.includes(role)"
|
||||
>
|
||||
<div class="role-cb-content">
|
||||
<div class="cnsl-chip-dot" [style.background]="getColor(role)"></div>
|
||||
<span>{{role | roletransform}}</span>
|
||||
<span>{{ role | roletransform }}</span>
|
||||
</div>
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
<div mat-dialog-actions class="action">
|
||||
<button color="primary" mat-stroked-button class="close-button" (click)="closeDialog()">
|
||||
{{'ACTIONS.CLOSE' | translate}}
|
||||
<button mat-stroked-button class="close-button" (click)="closeDialog()">
|
||||
{{ 'ACTIONS.CLOSE' | translate }}
|
||||
</button>
|
||||
<button color="primary" mat-raised-button class="ok-button" (click)="closeDialogWithRoles()">
|
||||
{{'ACTIONS.CHANGE' | translate}}
|
||||
{{ 'ACTIONS.CHANGE' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,20 +0,0 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
|
||||
import { FeaturesComponent } from './features.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: FeaturesComponent,
|
||||
data: {
|
||||
animation: 'DetailPage',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class FeaturesRoutingModule { }
|
@@ -1,312 +0,0 @@
|
||||
<cnsl-detail-layout [hasBackButton]="true" [title]="('FEATURES.TITLE' | translate)">
|
||||
<p class="subinfo" sub>
|
||||
<span class="cnsl-secondary-text">{{'FEATURES.DESCRIPTION' | translate}}</span>
|
||||
<a mat-icon-button href="https://docs.zitadel.ch/docs/legal/introduction" target="_blank">
|
||||
<i class="las la-info-circle"></i>
|
||||
</a>
|
||||
</p>
|
||||
<h2>{{'FEATURES.TIER.TITLE' | translate}}</h2>
|
||||
<p *ngIf="serviceType === FeatureServiceType.MGMT" class="tier-desc cnsl-secondary-text">{{'FEATURES.TIER.DESCRIPTION'
|
||||
| translate}}
|
||||
{{'FEATURES.TIER.QUESTIONS' | translate}} <a href="mailto:support@zitadel.ch">support@zitadel.ch</a>.</p>
|
||||
|
||||
<div class="detail">
|
||||
<p class="title cnsl-secondary-text">{{'FEATURES.TIER.NAME' | translate}}</p>
|
||||
<p class="center">{{features?.tier?.name}}
|
||||
<a class="ext" href="https://zitadel.ch/pricing" target="_blank">
|
||||
<i class="las la-external-link-alt"></i>
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="serviceType === FeatureServiceType.MGMT">
|
||||
<mat-spinner class="spinner" diameter="20" *ngIf="customerLoading || stripeLoading"></mat-spinner>
|
||||
<div class="detail" *ngIf="stripeCustomer || stripeCustomer === null">
|
||||
<p class="title cnsl-secondary-text">{{'FEATURES.TIER.DETAILS' | translate}}
|
||||
<a (click)="setCustomer()">{{'ACTIONS.EDIT' | translate}}</a>
|
||||
</p>
|
||||
<p>{{stripeCustomer?.contact}}</p>
|
||||
<p *ngIf="stripeCustomer?.company">{{stripeCustomer?.company}}</p>
|
||||
<p>{{stripeCustomer?.address}}</p>
|
||||
<p *ngIf="stripeCustomer?.postal_code || stripeCustomer?.city || stripeCustomer?.country">
|
||||
{{stripeCustomer?.postal_code}} {{stripeCustomer?.city}} {{stripeCustomer?.country}}
|
||||
<img *ngIf="customerCountry" height="20px" width="30px"
|
||||
style="margin-right: 1rem; border-radius: 2px; vertical-align: middle;"
|
||||
src="../../../assets/flags/{{customerCountry.isoCode.toLowerCase()}}.png" />
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<p class="error-tier-message" *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" alt="change tier">{{'FEATURES.TIER.BTN' | translate}}</a>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<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}}
|
||||
</button>
|
||||
</ng-template>
|
||||
|
||||
<cnsl-info-section *ngIf="isDefault"> {{'POLICY.DEFAULTLABEL' | translate}}</cnsl-info-section>
|
||||
<div class="content" *ngIf="features">
|
||||
<div class="row">
|
||||
<span class="left-desc">{{'FEATURES.DATA.AUDITLOGRETENTION' | translate}}</span>
|
||||
<span class="fill-space"></span>
|
||||
<span>{{features.auditLogRetention | timestampToRetention }} {{'FEATURES.RETENTIONDAYS' |
|
||||
translate}}</span>
|
||||
</div>
|
||||
|
||||
<p class="feature-section cnsl-secondary-text">{{'FEATURES.HEADERS.LOGINPOLICY' | translate}}</p>
|
||||
|
||||
<div class="row">
|
||||
<div class="featureavatar green">
|
||||
<i class="icon las la-sign-in-alt"></i>
|
||||
</div>
|
||||
<span class="left-desc">{{'FEATURES.DATA.LOGINPOLICYUSERNAMELOGIN' | translate}}</span>
|
||||
<span class="fill-space"></span>
|
||||
<template [ngTemplateOutlet]="templateRef"
|
||||
[ngTemplateOutletContext]="{active: features.loginPolicyUsernameLogin}"></template>
|
||||
<mat-slide-toggle class="toggle" color="primary" name="hasNumber" ngDefaultControl
|
||||
[(ngModel)]="features.loginPolicyUsernameLogin" *ngIf="(['iam.features.write'] | hasRole | async)">
|
||||
</mat-slide-toggle>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="featureavatar green">
|
||||
<i class="icon las la-sign-in-alt"></i>
|
||||
</div>
|
||||
<span class="left-desc">{{'FEATURES.DATA.LOGINPOLICYPASSWORDRESET' | translate}}</span>
|
||||
<span class="fill-space"></span>
|
||||
<template [ngTemplateOutlet]="templateRef"
|
||||
[ngTemplateOutletContext]="{active: features.loginPolicyPasswordReset}"></template>
|
||||
<mat-slide-toggle class="toggle" color="primary" name="hasNumber" ngDefaultControl
|
||||
[(ngModel)]="features.loginPolicyPasswordReset" *ngIf="(['iam.features.write'] | hasRole | async)">
|
||||
</mat-slide-toggle>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="featureavatar green">
|
||||
<i class="icon las la-sign-in-alt"></i>
|
||||
</div>
|
||||
<span class="left-desc">{{'FEATURES.DATA.LOGINPOLICYREGISTRATION' | translate}}</span>
|
||||
<span class="fill-space"></span>
|
||||
<template [ngTemplateOutlet]="templateRef"
|
||||
[ngTemplateOutletContext]="{active: features.loginPolicyRegistration}"></template>
|
||||
<mat-slide-toggle class="toggle" color="primary" name="hasNumber" ngDefaultControl
|
||||
[(ngModel)]="features.loginPolicyRegistration" *ngIf="(['iam.features.write'] | hasRole | async)">
|
||||
</mat-slide-toggle>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="featureavatar green">
|
||||
<i class="icon las la-sign-in-alt"></i>
|
||||
</div>
|
||||
<span class="left-desc">{{'FEATURES.DATA.LOGINPOLICYIDP' | translate}}</span>
|
||||
<span class="fill-space"></span>
|
||||
<template [ngTemplateOutlet]="templateRef"
|
||||
[ngTemplateOutletContext]="{active: features.loginPolicyIdp}"></template>
|
||||
<mat-slide-toggle class="toggle" color="primary" name="hasNumber" ngDefaultControl
|
||||
[(ngModel)]="features.loginPolicyIdp" *ngIf="(['iam.features.write'] | hasRole | async)">
|
||||
</mat-slide-toggle>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="featureavatar green">
|
||||
<i class="icon las la-sign-in-alt"></i>
|
||||
</div>
|
||||
<span class="left-desc">{{'FEATURES.DATA.LOGINPOLICYFACTORS' | translate}}</span>
|
||||
<span class="fill-space"></span>
|
||||
<template [ngTemplateOutlet]="templateRef"
|
||||
[ngTemplateOutletContext]="{active: features.loginPolicyFactors}"></template>
|
||||
<mat-slide-toggle class="toggle" color="primary" name="hasNumber" ngDefaultControl
|
||||
[(ngModel)]="features.loginPolicyFactors" *ngIf="(['iam.features.write'] | hasRole | async)">
|
||||
</mat-slide-toggle>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="featureavatar green">
|
||||
<i class="icon las la-sign-in-alt"></i>
|
||||
</div>
|
||||
<span class="left-desc">{{'FEATURES.DATA.LOGINPOLICYPASSWORDLESS' | translate}}</span>
|
||||
<span class="fill-space"></span>
|
||||
<template [ngTemplateOutlet]="templateRef"
|
||||
[ngTemplateOutletContext]="{active: features.loginPolicyPasswordless}"></template>
|
||||
<mat-slide-toggle class="toggle" color="primary" name="hasNumber" ngDefaultControl
|
||||
[(ngModel)]="features.loginPolicyPasswordless" *ngIf="(['iam.features.write'] | hasRole | async)">
|
||||
</mat-slide-toggle>
|
||||
</div>
|
||||
|
||||
<p class="feature-section cnsl-secondary-text">{{'FEATURES.HEADERS.PASSWORD' | translate}}</p>
|
||||
|
||||
<div class="row">
|
||||
<div class="featureavatar yellow">
|
||||
<mat-icon class="icon smaller" svgIcon="mdi_textbox_password"></mat-icon>
|
||||
</div>
|
||||
|
||||
<span class="left-desc">{{'FEATURES.DATA.LOGINPOLICYCOMPLEXITYPOLICY' | translate}}</span>
|
||||
<span class="fill-space"></span>
|
||||
<template [ngTemplateOutlet]="templateRef"
|
||||
[ngTemplateOutletContext]="{active: features.passwordComplexityPolicy}"></template>
|
||||
<mat-slide-toggle class="toggle" color="primary" name="hasNumber" ngDefaultControl
|
||||
[(ngModel)]="features.passwordComplexityPolicy" *ngIf="(['iam.features.write'] | hasRole | async)">
|
||||
</mat-slide-toggle>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="featureavatar yellow">
|
||||
<mat-icon class="icon smaller" svgIcon="mdi_textbox_password"></mat-icon>
|
||||
</div>
|
||||
|
||||
<span class="left-desc">{{'FEATURES.DATA.LOCKOUTPOLICY' | translate}}</span>
|
||||
<span class="fill-space"></span>
|
||||
<template [ngTemplateOutlet]="templateRef"
|
||||
[ngTemplateOutletContext]="{active: features.lockoutPolicy}"></template>
|
||||
<mat-slide-toggle class="toggle" color="primary" name="hasNumber" ngDefaultControl
|
||||
[(ngModel)]="features.lockoutPolicy" *ngIf="(['iam.features.write'] | hasRole | async)">
|
||||
</mat-slide-toggle>
|
||||
</div>
|
||||
|
||||
<p class="feature-section cnsl-secondary-text">{{'FEATURES.HEADERS.LABELPOLICY' | translate}}</p>
|
||||
|
||||
<div class="row">
|
||||
<div class="featureavatar blue">
|
||||
<i class="icon las la-swatchbook"></i>
|
||||
</div>
|
||||
<span class="left-desc">{{'FEATURES.DATA.LABELPOLICYPRIVATELABEL' | translate}}</span>
|
||||
<span class="fill-space"></span>
|
||||
<template [ngTemplateOutlet]="templateRef"
|
||||
[ngTemplateOutletContext]="{active: features.labelPolicyPrivateLabel}"></template>
|
||||
<mat-slide-toggle class="toggle" color="primary" name="hasNumber" ngDefaultControl
|
||||
[(ngModel)]="features.labelPolicyPrivateLabel" *ngIf="(['iam.features.write'] | hasRole | async)">
|
||||
</mat-slide-toggle>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="featureavatar blue">
|
||||
<i class="icon las la-swatchbook"></i>
|
||||
</div>
|
||||
<span class="left-desc">{{'FEATURES.DATA.LABELPOLICYWATERMARK' | translate}}</span>
|
||||
<span class="fill-space"></span>
|
||||
<template [ngTemplateOutlet]="templateRef"
|
||||
[ngTemplateOutletContext]="{active: features.labelPolicyWatermark}"></template>
|
||||
<mat-slide-toggle class="toggle" color="primary" name="hasNumber" ngDefaultControl
|
||||
[(ngModel)]="features.labelPolicyWatermark" *ngIf="(['iam.features.write'] | hasRole | async)">
|
||||
</mat-slide-toggle>
|
||||
</div>
|
||||
|
||||
<p class="feature-section cnsl-secondary-text">{{'FEATURES.HEADERS.DOMAIN' | translate}}</p>
|
||||
|
||||
<div class="row">
|
||||
<div class="featureavatar purple">
|
||||
<i class="icon las la-gem"></i>
|
||||
</div>
|
||||
<span class="left-desc">{{'FEATURES.DATA.CUSTOMDOMAIN' | translate}}</span>
|
||||
<span class="fill-space"></span>
|
||||
<template [ngTemplateOutlet]="templateRef" [ngTemplateOutletContext]="{active: features.customDomain}"></template>
|
||||
<mat-slide-toggle class="toggle" color="primary" name="hasNumber" ngDefaultControl
|
||||
[(ngModel)]="features.customDomain" *ngIf="(['iam.features.write'] | hasRole | async)">
|
||||
</mat-slide-toggle>
|
||||
</div>
|
||||
|
||||
<p class="feature-section cnsl-secondary-text">{{'FEATURES.HEADERS.TEXTSANDLINKS' | translate}}</p>
|
||||
|
||||
<div class="row">
|
||||
<div class="featureavatar red">
|
||||
<i class="icon las la-paragraph"></i>
|
||||
</div>
|
||||
<span class="left-desc">{{'FEATURES.DATA.CUSTOMTEXTMESSAGE' | translate}}</span>
|
||||
<span class="fill-space"></span>
|
||||
<template [ngTemplateOutlet]="templateRef"
|
||||
[ngTemplateOutletContext]="{active: features.customTextMessage}"></template>
|
||||
<mat-slide-toggle class="toggle" color="primary" name="hasNumber" ngDefaultControl
|
||||
[(ngModel)]="features.customTextMessage" *ngIf="(['iam.features.write'] | hasRole | async)">
|
||||
</mat-slide-toggle>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="featureavatar red">
|
||||
<i class="icon las la-paragraph"></i>
|
||||
</div>
|
||||
<span class="left-desc">{{'FEATURES.DATA.CUSTOMTEXTLOGIN' | translate}}</span>
|
||||
<span class="fill-space"></span>
|
||||
<template [ngTemplateOutlet]="templateRef"
|
||||
[ngTemplateOutletContext]="{active: features.customTextLogin}"></template>
|
||||
<mat-slide-toggle class="toggle" color="primary" name="hasNumber" ngDefaultControl
|
||||
[(ngModel)]="features.customTextLogin" *ngIf="(['iam.features.write'] | hasRole | async)">
|
||||
</mat-slide-toggle>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="featureavatar black">
|
||||
<i class="icon las la-file-contract"></i>
|
||||
</div>
|
||||
<span class="left-desc">{{'FEATURES.DATA.PRIVACYPOLICY' | translate}}</span>
|
||||
<span class="fill-space"></span>
|
||||
<template [ngTemplateOutlet]="templateRef"
|
||||
[ngTemplateOutletContext]="{active: features.privacyPolicy}"></template>
|
||||
<mat-slide-toggle class="toggle" color="primary" name="hasNumber" ngDefaultControl
|
||||
[(ngModel)]="features.privacyPolicy" *ngIf="(['iam.features.write'] | hasRole | async)">
|
||||
</mat-slide-toggle>
|
||||
</div>
|
||||
|
||||
<p class="feature-section cnsl-secondary-text">{{'FEATURES.HEADERS.METADATA' | translate}}</p>
|
||||
|
||||
<div class="row">
|
||||
<div class="featureavatar blue">
|
||||
<i class="icon las la-tags"></i>
|
||||
</div>
|
||||
<span class="left-desc">{{'FEATURES.DATA.METADATAUSER' | translate}}</span>
|
||||
<span class="fill-space"></span>
|
||||
<template [ngTemplateOutlet]="templateRef" [ngTemplateOutletContext]="{active: features.metadataUser}"></template>
|
||||
<mat-slide-toggle class="toggle" color="primary" name="hasNumber" ngDefaultControl
|
||||
[(ngModel)]="features.metadataUser" *ngIf="(['iam.features.write'] | hasRole | async)">
|
||||
</mat-slide-toggle>
|
||||
</div>
|
||||
|
||||
<p class="feature-section">{{'FEATURES.HEADERS.FLOWS' | translate}}</p>
|
||||
|
||||
<div class="row">
|
||||
<div class="featureavatar pink">
|
||||
<i class="icon las la-exchange-alt"></i>
|
||||
</div>
|
||||
<span class="left-desc">{{'FEATURES.DATA.FLOWS' | translate}}</span>
|
||||
<span class="fill-space"></span>
|
||||
<template [ngTemplateOutlet]="templateRef" [ngTemplateOutletContext]="{active: features.actions}"></template>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<span class="fill-space"></span>
|
||||
<cnsl-form-field class="flow-select">
|
||||
<cnsl-label>{{ 'FEATURES.DATA.FLOW.TYPE' | translate }}</cnsl-label>
|
||||
<mat-select [(ngModel)]="features.actionsAllowed"
|
||||
[disabled]="(['iam.features.write'] | hasRole | async) === false">
|
||||
<mat-option *ngFor="let allowedType of actionsSelection" [value]="allowedType">
|
||||
{{ 'FEATURES.DATA.FLOW.ACTIONSALLOWED.'+allowedType | translate}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</cnsl-form-field>
|
||||
|
||||
<cnsl-form-field *ngIf="features.actionsAllowed === ActionsAllowed.ACTIONS_ALLOWED_MAX" class="flow-count">
|
||||
<cnsl-label>{{ 'FEATURES.DATA.FLOW.COUNT' | translate }}</cnsl-label>
|
||||
<input cnslInput type="number" [(ngModel)]="features.maxActions"
|
||||
[disabled]="(['iam.features.write'] | hasRole | async) === false" ngDefaultControl />
|
||||
</cnsl-form-field>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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>
|
||||
</cnsl-detail-layout>
|
||||
|
||||
<ng-template #templateRef let-active="active">
|
||||
<span class="state" [ngClass]="{'active': active, 'inactive': !active}">
|
||||
{{active ? ('FEATURES.AVAILABLE' | translate) : ('FEATURES.UNAVAILABLE' | translate)}}
|
||||
</span>
|
||||
</ng-template>
|
@@ -1,181 +0,0 @@
|
||||
.subinfo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
margin: -1.5rem 0 0 0;
|
||||
|
||||
i {
|
||||
font-size: 1.2rem;
|
||||
height: 1.2rem;
|
||||
line-height: 1.2rem;
|
||||
}
|
||||
}
|
||||
|
||||
.tier-desc {
|
||||
font-size: 14px;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.detail {
|
||||
margin-bottom: 1rem;
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 14px;
|
||||
margin-bottom: 0.5rem;
|
||||
|
||||
a {
|
||||
margin-left: 0.5rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.center {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
a {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
height: 15px;
|
||||
width: auto;
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.spinner {
|
||||
margin: 0.5rem;
|
||||
}
|
||||
|
||||
.error-tier-message {
|
||||
color: var(--warn);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.current-tier {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding-top: 1rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
|
||||
.feature-section {
|
||||
font-size: 14px;
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0.3rem 0;
|
||||
|
||||
.featureavatar {
|
||||
margin-right: 1rem;
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
min-width: 30px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: linear-gradient(40deg, rgb(129, 85, 185) 30%, #7b8ada);
|
||||
|
||||
&.purple {
|
||||
background: linear-gradient(40deg, #7c3aed 30%, #6d28d9);
|
||||
}
|
||||
|
||||
&.red {
|
||||
background: linear-gradient(40deg, #dc2626 30%, #db2777);
|
||||
}
|
||||
|
||||
&.green {
|
||||
background: linear-gradient(40deg, #059669 30%, #047857);
|
||||
}
|
||||
|
||||
&.blue {
|
||||
background: linear-gradient(40deg, #3b82f6 30%, #4f46e5);
|
||||
}
|
||||
|
||||
&.pink {
|
||||
background: linear-gradient(40deg, #db2777 30%, #be185d);
|
||||
}
|
||||
|
||||
&.yellow {
|
||||
background: linear-gradient(40deg, #f59e0b 30%, #b45309);
|
||||
}
|
||||
|
||||
&.black {
|
||||
background: linear-gradient(40deg, #1f2937, #111827);
|
||||
}
|
||||
|
||||
i,
|
||||
.icon {
|
||||
font-size: 1.5rem;
|
||||
height: 1.5rem;
|
||||
line-height: 1.5rem;
|
||||
color: white;
|
||||
|
||||
&.smaller {
|
||||
font-size: 1.2rem;
|
||||
height: 1.2rem;
|
||||
line-height: 1.2rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.left-desc {
|
||||
font-size: 0.9rem;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
.fill-space {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.length-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.flow-select {
|
||||
flex-shrink: 1;
|
||||
min-width: 150px;
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
.flow-count {
|
||||
flex-shrink: 1;
|
||||
width: 70px;
|
||||
margin-left: 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.btn-container {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
width: 100%;
|
||||
|
||||
button {
|
||||
margin-top: 3rem;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.toggle {
|
||||
margin-left: 1rem;
|
||||
}
|
@@ -1,25 +0,0 @@
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
|
||||
import { FeaturesComponent } from './features.component';
|
||||
|
||||
describe('FeaturesComponent', () => {
|
||||
let component: FeaturesComponent;
|
||||
let fixture: ComponentFixture<FeaturesComponent>;
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [FeaturesComponent],
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(FeaturesComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@@ -1,274 +0,0 @@
|
||||
import { Component, Injector, OnDestroy, Type } from '@angular/core';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { Subscription } from 'rxjs';
|
||||
import {
|
||||
GetOrgFeaturesResponse,
|
||||
SetDefaultFeaturesRequest,
|
||||
SetOrgFeaturesRequest,
|
||||
} from 'src/app/proto/generated/zitadel/admin_pb';
|
||||
import { ActionsAllowed, Features } from 'src/app/proto/generated/zitadel/features_pb';
|
||||
import { GetFeaturesResponse } from 'src/app/proto/generated/zitadel/management_pb';
|
||||
import { Org } from 'src/app/proto/generated/zitadel/org_pb';
|
||||
import { AdminService } from 'src/app/services/admin.service';
|
||||
import { Breadcrumb, BreadcrumbService, BreadcrumbType } from 'src/app/services/breadcrumb.service';
|
||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
import { StorageKey, StorageLocation, StorageService } from 'src/app/services/storage.service';
|
||||
import { StripeCustomer, SubscriptionService } from 'src/app/services/subscription.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
import { COUNTRIES, Country } from './country';
|
||||
import { PaymentInfoDialogComponent } from './payment-info-dialog/payment-info-dialog.component';
|
||||
|
||||
export enum FeatureServiceType {
|
||||
MGMT,
|
||||
ADMIN,
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'cnsl-features',
|
||||
templateUrl: './features.component.html',
|
||||
styleUrls: ['./features.component.scss'],
|
||||
})
|
||||
export class FeaturesComponent implements OnDestroy {
|
||||
private managementService!: ManagementService;
|
||||
public serviceType!: FeatureServiceType;
|
||||
|
||||
public features!: Features.AsObject;
|
||||
|
||||
private sub: Subscription = new Subscription();
|
||||
public org!: Org.AsObject;
|
||||
|
||||
public FeatureServiceType: any = FeatureServiceType;
|
||||
|
||||
public customerLoading: boolean = false;
|
||||
public stripeLoading: boolean = false;
|
||||
public stripeURL: string = '';
|
||||
public stripeCustomer!: StripeCustomer;
|
||||
|
||||
public actionsSelection: any = [
|
||||
ActionsAllowed.ACTIONS_ALLOWED_NOT_ALLOWED,
|
||||
ActionsAllowed.ACTIONS_ALLOWED_MAX,
|
||||
ActionsAllowed.ACTIONS_ALLOWED_UNLIMITED,
|
||||
];
|
||||
public ActionsAllowed: any = ActionsAllowed;
|
||||
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private toast: ToastService,
|
||||
private storage: StorageService,
|
||||
private injector: Injector,
|
||||
private adminService: AdminService,
|
||||
private subService: SubscriptionService,
|
||||
private dialog: MatDialog,
|
||||
breadcrumbService: BreadcrumbService,
|
||||
) {
|
||||
const iamBread = new Breadcrumb({
|
||||
type: BreadcrumbType.IAM,
|
||||
name: 'IAM',
|
||||
routerLink: ['/system'],
|
||||
});
|
||||
const bread: Breadcrumb = {
|
||||
type: BreadcrumbType.ORG,
|
||||
routerLink: ['/org'],
|
||||
};
|
||||
breadcrumbService.setBreadcrumb([iamBread, bread]);
|
||||
|
||||
const temporg: Org.AsObject | null = this.storage.getItem(StorageKey.organization, StorageLocation.session);
|
||||
|
||||
if (temporg) {
|
||||
this.org = temporg;
|
||||
}
|
||||
|
||||
const serviceType = this.route.snapshot.data.serviceType;
|
||||
if (serviceType !== undefined) {
|
||||
this.serviceType = serviceType;
|
||||
if (this.serviceType === FeatureServiceType.MGMT) {
|
||||
this.managementService = this.injector.get(ManagementService as Type<ManagementService>);
|
||||
}
|
||||
|
||||
if (this.serviceType === FeatureServiceType.MGMT) {
|
||||
this.customerLoading = true;
|
||||
this.subService
|
||||
.getCustomer(this.org.id)
|
||||
.then((payload) => {
|
||||
this.customerLoading = false;
|
||||
this.stripeCustomer = payload;
|
||||
if (this.customerValid) {
|
||||
this.getLinkToStripe();
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
this.customerLoading = false;
|
||||
console.error(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.fetchData();
|
||||
}
|
||||
|
||||
public ngOnDestroy(): void {
|
||||
this.sub.unsubscribe();
|
||||
}
|
||||
|
||||
public setCustomer(): void {
|
||||
const dialogRefPhone = this.dialog.open(PaymentInfoDialogComponent, {
|
||||
data: {
|
||||
customer: this.stripeCustomer,
|
||||
},
|
||||
width: '400px',
|
||||
});
|
||||
|
||||
dialogRefPhone.afterClosed().subscribe((customer) => {
|
||||
if (customer) {
|
||||
this.stripeCustomer = customer;
|
||||
this.subService
|
||||
.setCustomer(this.org.id, customer)
|
||||
.then(() => {
|
||||
this.getLinkToStripe();
|
||||
})
|
||||
.catch(console.error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public getLinkToStripe(): void {
|
||||
if (this.serviceType === FeatureServiceType.MGMT) {
|
||||
this.stripeLoading = true;
|
||||
this.subService
|
||||
.getLink(this.org.id, window.location.href)
|
||||
.then((payload) => {
|
||||
this.stripeLoading = false;
|
||||
this.stripeURL = payload.redirect_url;
|
||||
})
|
||||
.catch((error) => {
|
||||
this.stripeLoading = false;
|
||||
console.error(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public fetchData(): void {
|
||||
this.getData().then((resp) => {
|
||||
if (resp?.features) {
|
||||
this.features = resp.features;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public savePolicy(): void {
|
||||
switch (this.serviceType) {
|
||||
case FeatureServiceType.MGMT:
|
||||
const req = new SetOrgFeaturesRequest();
|
||||
req.setOrgId(this.org.id);
|
||||
|
||||
req.setLoginPolicyUsernameLogin(this.features.loginPolicyUsernameLogin);
|
||||
req.setLoginPolicyPasswordReset(this.features.loginPolicyPasswordReset);
|
||||
req.setLoginPolicyRegistration(this.features.loginPolicyRegistration);
|
||||
req.setLoginPolicyIdp(this.features.loginPolicyIdp);
|
||||
req.setLoginPolicyFactors(this.features.loginPolicyFactors);
|
||||
req.setLoginPolicyPasswordless(this.features.loginPolicyPasswordless);
|
||||
req.setPasswordComplexityPolicy(this.features.passwordComplexityPolicy);
|
||||
req.setLabelPolicyPrivateLabel(this.features.labelPolicyPrivateLabel);
|
||||
req.setLabelPolicyWatermark(this.features.labelPolicyWatermark);
|
||||
req.setCustomDomain(this.features.customDomain);
|
||||
req.setCustomTextLogin(this.features.customTextLogin);
|
||||
req.setCustomTextMessage(this.features.customTextMessage);
|
||||
req.setPrivacyPolicy(this.features.privacyPolicy);
|
||||
req.setMetadataUser(this.features.metadataUser);
|
||||
req.setLockoutPolicy(this.features.lockoutPolicy);
|
||||
req.setActionsAllowed(this.features.actionsAllowed);
|
||||
req.setMaxActions(this.features.maxActions);
|
||||
|
||||
this.adminService
|
||||
.setOrgFeatures(req)
|
||||
.then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
break;
|
||||
case FeatureServiceType.ADMIN:
|
||||
// update Default org iam policy?
|
||||
const dreq = new SetDefaultFeaturesRequest();
|
||||
dreq.setLoginPolicyUsernameLogin(this.features.loginPolicyUsernameLogin);
|
||||
dreq.setLoginPolicyPasswordReset(this.features.loginPolicyPasswordReset);
|
||||
dreq.setLoginPolicyRegistration(this.features.loginPolicyRegistration);
|
||||
dreq.setLoginPolicyIdp(this.features.loginPolicyIdp);
|
||||
dreq.setLoginPolicyFactors(this.features.loginPolicyFactors);
|
||||
dreq.setLoginPolicyPasswordless(this.features.loginPolicyPasswordless);
|
||||
dreq.setPasswordComplexityPolicy(this.features.passwordComplexityPolicy);
|
||||
dreq.setLabelPolicyPrivateLabel(this.features.labelPolicyPrivateLabel);
|
||||
dreq.setLabelPolicyWatermark(this.features.labelPolicyWatermark);
|
||||
dreq.setCustomDomain(this.features.customDomain);
|
||||
dreq.setCustomTextLogin(this.features.customTextLogin);
|
||||
dreq.setCustomTextMessage(this.features.customTextMessage);
|
||||
dreq.setMetadataUser(this.features.metadataUser);
|
||||
dreq.setLockoutPolicy(this.features.lockoutPolicy);
|
||||
dreq.setActionsAllowed(this.features.actionsAllowed);
|
||||
dreq.setMaxActions(this.features.maxActions);
|
||||
|
||||
this.adminService
|
||||
.setDefaultFeatures(dreq)
|
||||
.then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.SET', true);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public resetFeatures(): void {
|
||||
if (this.serviceType === FeatureServiceType.MGMT) {
|
||||
this.adminService
|
||||
.resetOrgFeatures(this.org.id)
|
||||
.then(() => {
|
||||
this.toast.showInfo('POLICY.TOAST.RESETSUCCESS', true);
|
||||
setTimeout(() => {
|
||||
this.fetchData();
|
||||
}, 1000);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public get isDefault(): boolean {
|
||||
if (this.features && this.serviceType === FeatureServiceType.MGMT) {
|
||||
return this.features.isDefault;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
get customerValid(): boolean {
|
||||
return (
|
||||
!!this.stripeCustomer?.contact &&
|
||||
!!this.stripeCustomer?.address &&
|
||||
!!this.stripeCustomer?.city &&
|
||||
!!this.stripeCustomer?.postal_code
|
||||
);
|
||||
}
|
||||
|
||||
get customerCountry(): Country | undefined {
|
||||
return COUNTRIES.find((country) => country.isoCode === this.stripeCustomer.country);
|
||||
}
|
||||
}
|
@@ -1,56 +0,0 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
|
||||
import { DetailLayoutModule } from 'src/app/modules/detail-layout/detail-layout.module';
|
||||
import { InputModule } from 'src/app/modules/input/input.module';
|
||||
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module';
|
||||
import {
|
||||
TimestampToRetentionPipeModule,
|
||||
} from 'src/app/pipes/timestamp-to-retention-pipe/timestamp-to-retention-pipe.module';
|
||||
|
||||
import { FormFieldModule } from '../form-field/form-field.module';
|
||||
import { InfoSectionModule } from '../info-section/info-section.module';
|
||||
import { FeaturesRoutingModule } from './features-routing.module';
|
||||
import { FeaturesComponent } from './features.component';
|
||||
import { PaymentInfoDialogComponent } from './payment-info-dialog/payment-info-dialog.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
FeaturesComponent,
|
||||
PaymentInfoDialogComponent,
|
||||
],
|
||||
imports: [
|
||||
FeaturesRoutingModule,
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
InputModule,
|
||||
MatButtonModule,
|
||||
FormFieldModule,
|
||||
InputModule,
|
||||
HasRoleModule,
|
||||
MatSlideToggleModule,
|
||||
MatSelectModule,
|
||||
MatIconModule,
|
||||
HasRoleModule,
|
||||
HasRolePipeModule,
|
||||
MatTooltipModule,
|
||||
MatProgressSpinnerModule,
|
||||
InfoSectionModule,
|
||||
TranslateModule,
|
||||
DetailLayoutModule,
|
||||
TimestampToRetentionPipeModule,
|
||||
],
|
||||
exports: [
|
||||
FeaturesComponent,
|
||||
],
|
||||
})
|
||||
export class FeaturesModule { }
|
@@ -1,59 +0,0 @@
|
||||
<h1 mat-dialog-title>
|
||||
<span class="title">{{'FEATURES.TIER.DETAILS' | translate}} {{data?.number}}</span>
|
||||
</h1>
|
||||
<p class="desc cnsl-secondary-text">{{'FEATURES.TIER.DETAILS_DESC' | translate}}</p>
|
||||
<mat-spinner class="spinner" *ngIf="stripeLoading" diameter="20"></mat-spinner>
|
||||
|
||||
<div mat-dialog-content>
|
||||
<form [formGroup]="form">
|
||||
<cnsl-form-field class="formfield">
|
||||
<cnsl-label>{{ 'FEATURES.TIER.CONTACT' | translate }}</cnsl-label>
|
||||
<input cnslInput name="contact" formControlName="contact" />
|
||||
</cnsl-form-field>
|
||||
|
||||
<cnsl-form-field class="formfield">
|
||||
<cnsl-label>{{ 'FEATURES.TIER.COMPANY' | translate }}</cnsl-label>
|
||||
<input cnslInput name="company" formControlName="company" />
|
||||
</cnsl-form-field>
|
||||
|
||||
<cnsl-form-field class="formfield">
|
||||
<cnsl-label>{{ 'FEATURES.TIER.ADDRESS' | translate }}</cnsl-label>
|
||||
<input cnslInput name="address" formControlName="address" />
|
||||
</cnsl-form-field>
|
||||
|
||||
<cnsl-form-field class="formfield">
|
||||
<cnsl-label>{{ 'FEATURES.TIER.CITY' | translate }}</cnsl-label>
|
||||
<input cnslInput name="city" formControlName="city" />
|
||||
</cnsl-form-field>
|
||||
|
||||
<cnsl-form-field class="formfield">
|
||||
<cnsl-label>{{ 'FEATURES.TIER.POSTAL_CODE' | translate }}</cnsl-label>
|
||||
<input cnslInput name="postal_code" formControlName="postal_code" />
|
||||
</cnsl-form-field>
|
||||
|
||||
<cnsl-form-field class="formfield">
|
||||
<cnsl-label>{{ 'FEATURES.TIER.COUNTRY' | translate }}</cnsl-label>
|
||||
<mat-select formControlName="country" name="country" (selectionChange)="changeCountry($event)">
|
||||
<mat-option *ngFor="let country of COUNTRIES" [value]="country.isoCode" class="center-row">
|
||||
<div class="center">
|
||||
<div class="img-wrapper">
|
||||
<img height="20px" width="30px" style="margin-right: 1rem; border-radius: 2px; vertical-align: middle;"
|
||||
src="../../../../assets/flags/{{country.isoCode.toLowerCase()}}.png" />
|
||||
</div>
|
||||
{{country.isoCode}} <span class="lighter cnsl-secondary-text">| {{country.name}}</span>
|
||||
</div>
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</cnsl-form-field>
|
||||
</form>
|
||||
</div>
|
||||
<div mat-dialog-actions class="action">
|
||||
<button cdkFocusInitial mat-stroked-button class="ok-button" (click)="dialogRef.close()">
|
||||
{{'ACTIONS.CLOSE' | translate}}
|
||||
</button>
|
||||
|
||||
<button [disabled]="form.invalid" cdkFocusInitial color="primary" mat-raised-button class="ok-button"
|
||||
(click)="submitAndCloseDialog()">
|
||||
{{'ACTIONS.OK' | translate}}
|
||||
</button>
|
||||
</div>
|
@@ -1,45 +0,0 @@
|
||||
h1 {
|
||||
font-size: 1.5rem;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.desc {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.formfield {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.action {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
.ok-button {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
button {
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
height: 15px;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.img-wrapper {
|
||||
width: 50px;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.lighter {
|
||||
font-size: 12px;
|
||||
padding: 0 0.5rem;
|
||||
}
|
||||
|
||||
.center {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
@@ -1,25 +0,0 @@
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
|
||||
import { PaymentInfoDialogComponent } from './payment-info-dialog.component';
|
||||
|
||||
describe('PaymentInfoDialogComponent', () => {
|
||||
let component: PaymentInfoDialogComponent;
|
||||
let fixture: ComponentFixture<PaymentInfoDialogComponent>;
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [PaymentInfoDialogComponent],
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(PaymentInfoDialogComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@@ -1,91 +0,0 @@
|
||||
import { Component, Inject } from '@angular/core';
|
||||
import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { MatSelectChange } from '@angular/material/select';
|
||||
import { SubscriptionService } from 'src/app/services/subscription.service';
|
||||
|
||||
import { COUNTRIES, Country } from '../country';
|
||||
|
||||
function compare(a: Country, b: Country): number {
|
||||
if (a.isoCode < b.isoCode) {
|
||||
return -1;
|
||||
}
|
||||
if (a.isoCode > b.isoCode) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'cnsl-payment-info-dialog',
|
||||
templateUrl: './payment-info-dialog.component.html',
|
||||
styleUrls: ['./payment-info-dialog.component.scss'],
|
||||
})
|
||||
export class PaymentInfoDialogComponent {
|
||||
public stripeLoading: boolean = false;
|
||||
public COUNTRIES: Country[] = COUNTRIES.sort(compare);
|
||||
public form!: FormGroup;
|
||||
|
||||
private orgId: string = '';
|
||||
|
||||
constructor(
|
||||
private subService: SubscriptionService,
|
||||
private fb: FormBuilder,
|
||||
public dialogRef: MatDialogRef<PaymentInfoDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public data: any,
|
||||
) {
|
||||
this.orgId = data.orgId;
|
||||
|
||||
this.form = this.fb.group({
|
||||
contact: ['', [Validators.required]],
|
||||
company: ['', []],
|
||||
address: ['', [Validators.required]],
|
||||
city: ['', [Validators.required]],
|
||||
postal_code: ['', [Validators.required]],
|
||||
country: ['', [Validators.required]],
|
||||
});
|
||||
|
||||
if (data.customer) {
|
||||
this.form.patchValue(data.customer);
|
||||
}
|
||||
|
||||
if (!data.customer?.country) {
|
||||
this.form.get('country')?.setValue('CH');
|
||||
}
|
||||
|
||||
this.getLink();
|
||||
}
|
||||
|
||||
public getLink(): void {
|
||||
if (this.orgId) {
|
||||
this.stripeLoading = true;
|
||||
this.subService
|
||||
.getLink(this.orgId, window.location.href)
|
||||
.then((payload) => {
|
||||
this.stripeLoading = false;
|
||||
if (payload.redirect_url) {
|
||||
window.open(payload.redirect_url, '_blank');
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
this.stripeLoading = false;
|
||||
console.error(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public changeCountry(selection: MatSelectChange): void {
|
||||
const country = COUNTRIES.find((c) => c.isoCode === selection.value);
|
||||
if (country && country.phoneCode !== undefined && this.phone && this.phone.value !== `+${country.phoneCode}`) {
|
||||
this.phone.setValue(`+${country.phoneCode}`);
|
||||
}
|
||||
}
|
||||
|
||||
submitAndCloseDialog(): void {
|
||||
this.dialogRef.close(this.form.value);
|
||||
}
|
||||
|
||||
get phone(): AbstractControl | null {
|
||||
return this.form.get('phone');
|
||||
}
|
||||
}
|
@@ -2,26 +2,26 @@
|
||||
<div class="footer-row">
|
||||
<div class="footer-links">
|
||||
<a target="_blank" *ngIf="policy?.tosLink" rel="noreferrer" [href]="policy.tosLink" external>
|
||||
<span>{{'FOOTER.LINKS.TOS' | translate}}</span>
|
||||
<span>{{ 'FOOTER.LINKS.TOS' | translate }}</span>
|
||||
<i class="las la-external-link-alt"></i>
|
||||
</a>
|
||||
<a target="_blank" *ngIf="policy?.privacyLink" rel="noreferrer" [href]="policy.privacyLink" external>
|
||||
<span>{{'FOOTER.LINKS.PP' | translate}}</span>
|
||||
<span>{{ 'FOOTER.LINKS.PP' | translate }}</span>
|
||||
<i class="las la-external-link-alt"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="footer-socials" *ngIf="privateLabelPolicy && privateLabelPolicy.disableWatermark === false">
|
||||
<a target="_blank" rel="noreferrer" href="https://github.com/caos">
|
||||
<a target="_blank" rel="noreferrer" href="https://github.com/zitadel">
|
||||
<i class="text-3xl lab la-github"></i>
|
||||
</a>
|
||||
<a target="_blank" rel="noreferrer" href="https://twitter.com/caos_ch">
|
||||
<a target="_blank" rel="noreferrer" href="https://twitter.com/zitadel_ch">
|
||||
<i class="text-3xl lab la-twitter"></i>
|
||||
</a>
|
||||
<a target="_blank" rel="noreferrer" href="https://www.linkedin.com/company/zitadel/">
|
||||
<i class="text-3xl lab la-linkedin"></i>
|
||||
</a>
|
||||
<a target="_blank" rel="noreferrer" href="https://discord.gg/erh5Brh7jE">
|
||||
<a target="_blank" rel="noreferrer" href="https://zitadel.ch/chat">
|
||||
<i class="text-3xl lab la-discord"></i>
|
||||
</a>
|
||||
<a target="_blank" rel="noreferrer" href="https://www.youtube.com/channel/UCUAWJUNYaRn1yIVrZxEfsuA">
|
||||
@@ -98,4 +98,4 @@
|
||||
</a>
|
||||
</div> -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -1,137 +1,234 @@
|
||||
<mat-toolbar class="header-wrapper">
|
||||
<div class="header-content">
|
||||
<a class="title custom" [routerLink]="['/']"
|
||||
*ngIf="org && labelpolicy && labelpolicy.disableWatermark; else defaultHome">
|
||||
<img class="logo" alt="home logo" *ngIf="isDarkTheme; else customlighttheme"
|
||||
[src]="labelpolicy?.iconUrlDark ? labelpolicy.iconUrlDark : './assets/images/zitadel-logo-solo-light.svg'"
|
||||
onerror="this.src='./assets/images/zitadel-logo-solo-light.svg';this.onerror='';" />
|
||||
<ng-template #customlighttheme>
|
||||
<img alt="home logo" class="logo"
|
||||
[src]="labelpolicy?.iconUrl ? labelpolicy.iconUrl : './assets/images/zitadel-logo-solo-dark.svg'"
|
||||
onerror="this.src='./assets/images/zitadel-logo-solo-dark.svg';this.onerror='';" />
|
||||
</ng-template>
|
||||
</a>
|
||||
<div class="header-content">
|
||||
<a
|
||||
class="title custom"
|
||||
[routerLink]="['/']"
|
||||
*ngIf="org && labelpolicy && labelpolicy.disableWatermark; else defaultHome"
|
||||
>
|
||||
<img
|
||||
class="logo"
|
||||
alt="home logo"
|
||||
*ngIf="isDarkTheme; else customlighttheme"
|
||||
[src]="labelpolicy?.iconUrlDark ? labelpolicy.iconUrlDark : './assets/images/zitadel-logo-solo-light.svg'"
|
||||
onerror="this.src='./assets/images/zitadel-logo-solo-light.svg';this.onerror='';"
|
||||
/>
|
||||
<ng-template #customlighttheme>
|
||||
<img
|
||||
alt="home logo"
|
||||
class="logo"
|
||||
[src]="labelpolicy?.iconUrl ? labelpolicy.iconUrl : './assets/images/zitadel-logo-solo-dark.svg'"
|
||||
onerror="this.src='./assets/images/zitadel-logo-solo-dark.svg';this.onerror='';"
|
||||
/>
|
||||
</ng-template>
|
||||
</a>
|
||||
|
||||
<ng-template #defaultHome>
|
||||
<a *ngIf="org" class="title" [routerLink]="['/']">
|
||||
<img class="logo" alt="zitadel logo" *ngIf="isDarkTheme; 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" />
|
||||
</ng-template>
|
||||
</a>
|
||||
<ng-template #defaultHome>
|
||||
<a *ngIf="org" class="title" [routerLink]="['/']">
|
||||
<img
|
||||
class="logo"
|
||||
alt="zitadel logo"
|
||||
*ngIf="isDarkTheme; 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" />
|
||||
</ng-template>
|
||||
</a>
|
||||
</ng-template>
|
||||
|
||||
<ng-container *ngFor="let bread of (breadcrumbService.breadcrumbs$ | async) as bc; index as i">
|
||||
<ng-container *ngIf="bread.type === BreadcrumbType.IAM">
|
||||
<ng-template cnslHasRole [hasRole]="['iam.read']">
|
||||
<svg class="slash hide-on-small" viewBox="0 0 24 24" width="32" height="32" stroke="currentColor"
|
||||
stroke-width="1" stroke-linecap="round" stroke-linejoin="round"
|
||||
shape-rendering="geometricPrecision">
|
||||
<path d="M16.88 3.549L7.12 20.451"></path>
|
||||
</svg>
|
||||
<ng-container *ngFor="let bread of breadcrumbService.breadcrumbs$ | async as bc; index as i">
|
||||
<ng-container *ngIf="bread.type === BreadcrumbType.IAM">
|
||||
<ng-template cnslHasRole [hasRole]="['iam.read']">
|
||||
<svg
|
||||
class="slash hide-on-small"
|
||||
viewBox="0 0 24 24"
|
||||
width="32"
|
||||
height="32"
|
||||
stroke="currentColor"
|
||||
stroke-width="1"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
shape-rendering="geometricPrecision"
|
||||
>
|
||||
<path d="M16.88 3.549L7.12 20.451"></path>
|
||||
</svg>
|
||||
|
||||
<div class="breadcrumb-context hide-on-small">
|
||||
<a matRipple [matRippleUnbounded]="false" class="breadcrumb-link"
|
||||
[routerLink]="bread.routerLink">
|
||||
{{'MENU.SYSTEM' | translate}}
|
||||
</a>
|
||||
</div>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="bread.type === BreadcrumbType.ORG">
|
||||
<svg class="slash" viewBox="0 0 24 24" width="32" height="32" stroke="currentColor" stroke-width="1"
|
||||
stroke-linecap="round" stroke-linejoin="round" shape-rendering="geometricPrecision">
|
||||
<path d="M16.88 3.549L7.12 20.451"></path>
|
||||
</svg>
|
||||
|
||||
<div class="org-context">
|
||||
<a *ngIf="org" matRipple [matRippleUnbounded]="false" class="org-link" id="orglink"
|
||||
[routerLink]="[ '/org']">
|
||||
{{org?.name ? org.name : 'NO NAME'}}</a>
|
||||
|
||||
<div class="org-context-wrapper" *ngIf="org">
|
||||
<button cdkOverlayOrigin #trigger="cdkOverlayOrigin" matRipple [matRippleUnbounded]="false"
|
||||
id="orgswitchbutton" class="org-switch-button" (click)="showOrgContext = !showOrgContext">
|
||||
<span class="svgspan">
|
||||
<svg xmlns=" http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"
|
||||
aria-hidden="true">
|
||||
<path fill-rule="evenodd"
|
||||
d="M10 3a1 1 0 01.707.293l3 3a1 1 0 01-1.414 1.414L10 5.414 7.707 7.707a1 1 0 01-1.414-1.414l3-3A1 1 0 0110 3zm-3.707 9.293a1 1 0 011.414 0L10 14.586l2.293-2.293a1 1 0 011.414 1.414l-3 3a1 1 0 01-1.414 0l-3-3a1 1 0 010-1.414z"
|
||||
clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
</span>
|
||||
<cnsl-action-keys (actionTriggered)="showOrgContext = !showOrgContext"
|
||||
[type]="ActionKeysType.ORGSWITCHER"></cnsl-action-keys>
|
||||
</button>
|
||||
|
||||
<ng-template cdkConnectedOverlay [cdkConnectedOverlayOrigin]="trigger"
|
||||
[flexibleDimensions]="true" [lockPosition]="true" [cdkConnectedOverlayOffsetY]="10"
|
||||
[cdkConnectedOverlayHasBackdrop]="true" [cdkConnectedOverlayPositions]="positions"
|
||||
cdkConnectedOverlayBackdropClass="transparent-backdrop"
|
||||
[cdkConnectedOverlayOpen]="showOrgContext" (backdropClick)="showOrgContext = false"
|
||||
(detach)="showOrgContext = false">
|
||||
<cnsl-org-context class="context_card" *ngIf="org && showOrgContext"
|
||||
(closedCard)="showOrgContext = false" [org]="org" (setOrg)="setActiveOrg($event)">
|
||||
</cnsl-org-context>
|
||||
</ng-template>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="bread.type !== BreadcrumbType.IAM && bread.type !== BreadcrumbType.ORG">
|
||||
<svg class="slash" viewBox="0 0 24 24" width="32" height="32" stroke="currentColor" stroke-width="1"
|
||||
stroke-linecap="round" stroke-linejoin="round" shape-rendering="geometricPrecision">
|
||||
<path d="M16.88 3.549L7.12 20.451"></path>
|
||||
</svg>
|
||||
|
||||
<div class="breadcrumb-context">
|
||||
<a matRipple [matRippleUnbounded]="false" class="breadcrumb-link"
|
||||
[ngClass]="{'maxwidth': bc.length > 1}" [routerLink]="bread.routerLink">
|
||||
<ng-container *ngIf="i !== bc.length - 1; else defLabel">
|
||||
<span class="desk">{{bread.name}}</span>
|
||||
<span class="mob">...</span>
|
||||
</ng-container>
|
||||
<ng-template #defLabel>
|
||||
<span>{{bread.name}}</span>
|
||||
</ng-template>
|
||||
</a>
|
||||
</div>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
|
||||
<span class="fill-space"></span>
|
||||
|
||||
<a class="doc-link" href="https://docs.zitadel.ch" mat-stroked-button target="_blank">
|
||||
{{'MENU.DOCUMENTATION' | translate}}
|
||||
</a>
|
||||
|
||||
<div class="system-rel" *ngIf="!isOnMe">
|
||||
<a id="systembutton" *ngIf="!isOnSystem && (['iam.read$','iam.write$'] | hasRole | async)"
|
||||
[routerLink]="[ '/system']" class="iam-settings cnsl-action-button" mat-stroked-button>
|
||||
<span class="iam-label">{{'MENU.SYSTEM' | translate}}</span>
|
||||
<i class="las la-cog"></i>
|
||||
<div class="breadcrumb-context hide-on-small">
|
||||
<a matRipple [matRippleUnbounded]="false" class="breadcrumb-link" [routerLink]="bread.routerLink">
|
||||
{{ 'MENU.INSTANCE' | translate }}
|
||||
</a>
|
||||
<a id="orgbutton" *ngIf="isOnSystem && (['org.read'] | hasRole | async)" [routerLink]="[ '/org']"
|
||||
class="org-settings cnsl-action-button" mat-stroked-button>
|
||||
<span class="iam-label">{{'MENU.ORGANIZATION' | translate}}</span>
|
||||
<i class="las la-cog"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
|
||||
<div cnslOutsideClick (clickOutside)="closeAccountCard()" class="icon-container"
|
||||
[ngClass]="{'iam-user': (['iam.write$'] | hasRole | async)}">
|
||||
<cnsl-avatar id="avatartoggle"
|
||||
*ngIf="user && (user.human?.profile?.displayName || (user.human?.profile?.firstName && user.human?.profile?.lastName))"
|
||||
class="avatar-toggle 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">
|
||||
</cnsl-avatar>
|
||||
<cnsl-accounts-card @accounts class="a_card" *ngIf="showAccount" (closedCard)="showAccount = false"
|
||||
[user]="user" [iamuser]="(['iam.write$'] | hasRole) | async">
|
||||
</cnsl-accounts-card>
|
||||
<ng-container *ngIf="bread.type === BreadcrumbType.ORG">
|
||||
<svg
|
||||
class="slash"
|
||||
viewBox="0 0 24 24"
|
||||
width="32"
|
||||
height="32"
|
||||
stroke="currentColor"
|
||||
stroke-width="1"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
shape-rendering="geometricPrecision"
|
||||
>
|
||||
<path d="M16.88 3.549L7.12 20.451"></path>
|
||||
</svg>
|
||||
|
||||
<div class="org-context">
|
||||
<a *ngIf="org" matRipple [matRippleUnbounded]="false" class="org-link" id="orglink" [routerLink]="['/org']">
|
||||
{{ org?.name ? org.name : 'NO NAME' }}</a
|
||||
>
|
||||
|
||||
<div class="org-context-wrapper" *ngIf="org">
|
||||
<button
|
||||
cdkOverlayOrigin
|
||||
#trigger="cdkOverlayOrigin"
|
||||
matRipple
|
||||
[matRippleUnbounded]="false"
|
||||
id="orgswitchbutton"
|
||||
class="org-switch-button"
|
||||
(click)="showOrgContext = !showOrgContext"
|
||||
>
|
||||
<span class="svgspan">
|
||||
<svg xmlns=" http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M10 3a1 1 0 01.707.293l3 3a1 1 0 01-1.414 1.414L10 5.414 7.707 7.707a1 1 0 01-1.414-1.414l3-3A1 1 0 0110 3zm-3.707 9.293a1 1 0 011.414 0L10 14.586l2.293-2.293a1 1 0 011.414 1.414l-3 3a1 1 0 01-1.414 0l-3-3a1 1 0 010-1.414z"
|
||||
clip-rule="evenodd"
|
||||
></path>
|
||||
</svg>
|
||||
</span>
|
||||
<cnsl-action-keys
|
||||
(actionTriggered)="showOrgContext = !showOrgContext"
|
||||
[type]="ActionKeysType.ORGSWITCHER"
|
||||
></cnsl-action-keys>
|
||||
</button>
|
||||
|
||||
<ng-template
|
||||
cdkConnectedOverlay
|
||||
[cdkConnectedOverlayOrigin]="trigger"
|
||||
[flexibleDimensions]="true"
|
||||
[lockPosition]="true"
|
||||
[cdkConnectedOverlayOffsetY]="10"
|
||||
[cdkConnectedOverlayHasBackdrop]="true"
|
||||
[cdkConnectedOverlayPositions]="positions"
|
||||
cdkConnectedOverlayBackdropClass="transparent-backdrop"
|
||||
[cdkConnectedOverlayOpen]="showOrgContext"
|
||||
(backdropClick)="showOrgContext = false"
|
||||
(detach)="showOrgContext = false"
|
||||
>
|
||||
<cnsl-org-context
|
||||
class="context_card"
|
||||
*ngIf="org && showOrgContext"
|
||||
(closedCard)="showOrgContext = false"
|
||||
[org]="org"
|
||||
(setOrg)="setActiveOrg($event)"
|
||||
>
|
||||
</cnsl-org-context>
|
||||
</ng-template>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="bread.type !== BreadcrumbType.IAM && bread.type !== BreadcrumbType.ORG">
|
||||
<svg
|
||||
class="slash"
|
||||
viewBox="0 0 24 24"
|
||||
width="32"
|
||||
height="32"
|
||||
stroke="currentColor"
|
||||
stroke-width="1"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
shape-rendering="geometricPrecision"
|
||||
>
|
||||
<path d="M16.88 3.549L7.12 20.451"></path>
|
||||
</svg>
|
||||
|
||||
<div class="breadcrumb-context">
|
||||
<a
|
||||
matRipple
|
||||
[matRippleUnbounded]="false"
|
||||
class="breadcrumb-link"
|
||||
[ngClass]="{ maxwidth: bc.length > 1 }"
|
||||
[routerLink]="bread.routerLink"
|
||||
>
|
||||
<ng-container *ngIf="i !== bc.length - 1; else defLabel">
|
||||
<span class="desk">{{ bread.name }}</span>
|
||||
<span class="mob">...</span>
|
||||
</ng-container>
|
||||
<ng-template #defLabel>
|
||||
<span>{{ bread.name }}</span>
|
||||
</ng-template>
|
||||
</a>
|
||||
</div>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
|
||||
<span class="fill-space"></span>
|
||||
|
||||
<a class="doc-link" href="https://docs.zitadel.ch" mat-stroked-button target="_blank">
|
||||
{{ 'MENU.DOCUMENTATION' | translate }}
|
||||
</a>
|
||||
|
||||
<div class="system-rel" *ngIf="!isOnMe">
|
||||
<a
|
||||
id="systembutton"
|
||||
*ngIf="!isOnSystem && (['iam.read$', 'iam.write$'] | hasRole | async)"
|
||||
[routerLink]="['/system']"
|
||||
class="iam-settings cnsl-action-button"
|
||||
mat-stroked-button
|
||||
>
|
||||
<span class="iam-label">{{ 'MENU.INSTANCE' | translate }}</span>
|
||||
<i class="las la-cog"></i>
|
||||
</a>
|
||||
<a
|
||||
id="orgbutton"
|
||||
*ngIf="isOnSystem && (['org.read'] | hasRole | async)"
|
||||
[routerLink]="['/org']"
|
||||
class="org-settings cnsl-action-button"
|
||||
mat-stroked-button
|
||||
>
|
||||
<span class="iam-label">{{ 'MENU.ORGANIZATION' | translate }}</span>
|
||||
<i class="las la-cog"></i>
|
||||
</a>
|
||||
</div>
|
||||
</mat-toolbar>
|
||||
|
||||
<div
|
||||
cnslOutsideClick
|
||||
(clickOutside)="closeAccountCard()"
|
||||
class="icon-container"
|
||||
[ngClass]="{ 'iam-user': (['iam.write$'] | hasRole | async) }"
|
||||
>
|
||||
<cnsl-avatar
|
||||
id="avatartoggle"
|
||||
*ngIf="
|
||||
user && (user.human?.profile?.displayName || (user.human?.profile?.firstName && user.human?.profile?.lastName))
|
||||
"
|
||||
class="avatar-toggle 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"
|
||||
>
|
||||
</cnsl-avatar>
|
||||
<cnsl-accounts-card
|
||||
@accounts
|
||||
class="a_card"
|
||||
*ngIf="showAccount"
|
||||
(closedCard)="showAccount = false"
|
||||
[user]="user"
|
||||
[iamuser]="['iam.write$'] | hasRole | async"
|
||||
>
|
||||
</cnsl-accounts-card>
|
||||
</div>
|
||||
</div>
|
||||
</mat-toolbar>
|
||||
|
@@ -1,18 +1,35 @@
|
||||
<cnsl-refresh-table [loading]="loading$ | async" (refreshed)="refreshPage()" [dataSize]="dataSource.data.length"
|
||||
[emitRefreshOnPreviousRoutes]="['/system/idp/create']" [timestamp]="idpResult?.details?.viewTimestamp"
|
||||
[selection]="selection" [hideRefresh]="true">
|
||||
<cnsl-refresh-table
|
||||
[loading]="loading$ | async"
|
||||
(refreshed)="refreshPage()"
|
||||
[dataSize]="dataSource.data.length"
|
||||
[emitRefreshOnPreviousRoutes]="['/system/idp/create']"
|
||||
[timestamp]="idpResult?.details?.viewTimestamp"
|
||||
[selection]="selection"
|
||||
[hideRefresh]="true"
|
||||
>
|
||||
<div actions>
|
||||
<button (click)="deactivateSelectedIdps()" matTooltip="{{'IDP.DEACTIVATE' | translate}}"
|
||||
class="margin-right bg-state inactive" mat-stroked-button *ngIf="selection.hasValue()" [disabled]="disabled">
|
||||
{{'IDP.DEACTIVATE' | translate}}
|
||||
<button
|
||||
(click)="deactivateSelectedIdps()"
|
||||
matTooltip="{{ 'IDP.DEACTIVATE' | translate }}"
|
||||
class="margin-right bg-state inactive"
|
||||
mat-stroked-button
|
||||
*ngIf="selection.hasValue()"
|
||||
[disabled]="disabled"
|
||||
>
|
||||
{{ 'IDP.DEACTIVATE' | translate }}
|
||||
</button>
|
||||
<button (click)="reactivateSelectedIdps()" matTooltip="{{'IDP.ACTIVATE' | translate}}" class="bg-state active"
|
||||
mat-stroked-button *ngIf="selection.hasValue()" [disabled]="disabled">
|
||||
{{'IDP.ACTIVATE' | translate}}
|
||||
<button
|
||||
(click)="reactivateSelectedIdps()"
|
||||
matTooltip="{{ 'IDP.ACTIVATE' | translate }}"
|
||||
class="bg-state active"
|
||||
mat-stroked-button
|
||||
*ngIf="selection.hasValue()"
|
||||
[disabled]="disabled"
|
||||
>
|
||||
{{ 'IDP.ACTIVATE' | translate }}
|
||||
</button>
|
||||
|
||||
<a [routerLink]="createRouterLink" class="cnsl-action-button" color="primary" mat-raised-button
|
||||
[disabled]="disabled">
|
||||
<a [routerLink]="createRouterLink" class="cnsl-action-button" color="primary" mat-raised-button [disabled]="disabled">
|
||||
<mat-icon class="icon">add</mat-icon>{{ 'ACTIONS.NEW' | translate }}
|
||||
</a>
|
||||
</div>
|
||||
@@ -21,18 +38,29 @@
|
||||
<table class="table" mat-table [dataSource]="dataSource">
|
||||
<ng-container matColumnDef="select">
|
||||
<th mat-header-cell *matHeaderCellDef>
|
||||
<mat-checkbox color="primary" (change)="$event ? masterToggle() : null"
|
||||
<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"
|
||||
<mat-checkbox
|
||||
color="primary"
|
||||
(click)="$event.stopPropagation()"
|
||||
class="chbox"
|
||||
[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" />
|
||||
(change)="$event ? selection.toggle(idp) : null"
|
||||
[checked]="selection.isSelected(idp)"
|
||||
>
|
||||
<img
|
||||
src="../../../assets/images/google.png"
|
||||
*ngIf="idp.stylingType === IDPStylingType.IDPSTYLINGTYPE_GOOGLE"
|
||||
alt="google"
|
||||
/>
|
||||
</mat-checkbox>
|
||||
</td>
|
||||
</ng-container>
|
||||
@@ -42,76 +70,123 @@
|
||||
<!-- <span>{{ 'IDP.AVAILABILITY' | translate }}</span> -->
|
||||
</th>
|
||||
<td class="availability" [routerLink]="routerLinkForRow(idp)" mat-cell *matCellDef="let idp">
|
||||
<i matTooltip="{{'IDP.AVAILABLE' | translate}}"
|
||||
<i
|
||||
matTooltip="{{ 'IDP.AVAILABLE' | translate }}"
|
||||
*ngIf="isEnabled(idp) && idp.state === IDPState.IDP_STATE_ACTIVE"
|
||||
class="idp-available las la-check-circle"></i>
|
||||
<i matTooltip="{{'IDP.AVAILABLEBUTINACTIVE' | translate}}"
|
||||
class="idp-available las la-check-circle"
|
||||
></i>
|
||||
<i
|
||||
matTooltip="{{ 'IDP.AVAILABLEBUTINACTIVE' | translate }}"
|
||||
*ngIf="isEnabled(idp) && idp.state === IDPState.IDP_STATE_INACTIVE"
|
||||
class="idp-not-available las la-check-circle"></i>
|
||||
class="idp-not-available las la-check-circle"
|
||||
></i>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="name">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ 'IDP.NAME' | translate }} </th>
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'IDP.NAME' | translate }}</th>
|
||||
<td class="pointer" [routerLink]="routerLinkForRow(idp)" mat-cell *matCellDef="let idp">
|
||||
<span>{{idp?.name}}</span>
|
||||
<span>{{ idp?.name }}</span>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="type">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ 'IDP.TYPE' | translate }} </th>
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'IDP.TYPE' | translate }}</th>
|
||||
<td class="pointer" [routerLink]="routerLinkForRow(idp)" mat-cell *matCellDef="let idp">
|
||||
<span class="state" *ngIf="idp?.oidcConfig">{{'IDP.OIDC.TITLE' | translate}}</span>
|
||||
<span class="state" *ngIf="idp?.jwtConfig">{{'IDP.JWT.TITLE' | translate}}</span>
|
||||
<span class="state" *ngIf="idp?.oidcConfig">{{ 'IDP.OIDC.TITLE' | translate }}</span>
|
||||
<span class="state" *ngIf="idp?.jwtConfig">{{ 'IDP.JWT.TITLE' | translate }}</span>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="state">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ 'IDP.STATE' | translate }} </th>
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'IDP.STATE' | translate }}</th>
|
||||
<td class="pointer" [routerLink]="routerLinkForRow(idp)" mat-cell *matCellDef="let idp">
|
||||
<span class="state"
|
||||
[ngClass]="{'active': idp.state === IDPState.IDP_STATE_ACTIVE, 'inactive': idp.state === IDPState.IDP_STATE_INACTIVE}">{{
|
||||
'IDP.STATES.'+idp.state | translate }}</span>
|
||||
<span
|
||||
class="state"
|
||||
[ngClass]="{
|
||||
active: idp.state === IDPState.IDP_STATE_ACTIVE,
|
||||
inactive: idp.state === IDPState.IDP_STATE_INACTIVE
|
||||
}"
|
||||
>{{ 'IDP.STATES.' + idp.state | translate }}</span
|
||||
>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="creationDate">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ 'IDP.CREATIONDATE' | translate }} </th>
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'IDP.CREATIONDATE' | translate }}</th>
|
||||
<td [routerLink]="routerLinkForRow(idp)" class="pointer" mat-cell *matCellDef="let idp">
|
||||
<span>{{idp.details.creationDate | timestampToDate | localizedDate: 'dd. MMM, HH:mm' }}</span>
|
||||
<span>{{ idp.details.creationDate | timestampToDate | localizedDate: 'dd. MMM, HH:mm' }}</span>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="changeDate">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ 'IDP.CHANGEDATE' | translate }} </th>
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'IDP.CHANGEDATE' | translate }}</th>
|
||||
<td [routerLink]="routerLinkForRow(idp)" class="pointer" mat-cell *matCellDef="let idp">
|
||||
<span>{{idp.details.changeDate | timestampToDate | localizedDate: 'dd. MMM, HH:mm' }}</span>
|
||||
<span>{{ idp.details.changeDate | timestampToDate | localizedDate: 'dd. MMM, HH:mm' }}</span>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="owner">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ 'IDP.OWNER' | translate }} </th>
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'IDP.OWNER' | translate }}</th>
|
||||
<td [routerLink]="routerLinkForRow(idp)" class="pointer" mat-cell *matCellDef="let idp">
|
||||
{{'IDP.OWNERTYPES.'+idp.owner | translate }} </td>
|
||||
{{ 'IDP.OWNERTYPES.' + idp.owner | translate }}
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="actions" stickyEnd>
|
||||
<th class="idp-table-actions" mat-header-cell *matHeaderCellDef></th>
|
||||
<td class="idp-table-actions" mat-cell *matCellDef="let idp">
|
||||
<cnsl-table-actions>
|
||||
<button actions *ngIf="!isEnabled(idp)"
|
||||
[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))"
|
||||
mat-icon-button matTooltip="{{'IDP.SETAVAILABLE' | translate}}" (click)="addIdp(idp)">
|
||||
<button
|
||||
actions
|
||||
*ngIf="!isEnabled(idp)"
|
||||
[disabled]="
|
||||
([
|
||||
serviceType === PolicyComponentServiceType.ADMIN
|
||||
? 'iam.idp.write'
|
||||
: serviceType === PolicyComponentServiceType.MGMT
|
||||
? 'org.idp.write'
|
||||
: ''
|
||||
]
|
||||
| hasRole
|
||||
| async) === false
|
||||
"
|
||||
mat-icon-button
|
||||
matTooltip="{{ 'IDP.SETAVAILABLE' | translate }}"
|
||||
(click)="addIdp(idp)"
|
||||
>
|
||||
<i class="las la-check-circle"></i>
|
||||
</button>
|
||||
<button actions *ngIf="isEnabled(idp)"
|
||||
[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))"
|
||||
mat-icon-button matTooltip="{{'IDP.SETUNAVAILABLE' | translate}}" (click)="removeIdp(idp)">
|
||||
<button
|
||||
actions
|
||||
*ngIf="isEnabled(idp)"
|
||||
[disabled]="
|
||||
([
|
||||
serviceType === PolicyComponentServiceType.ADMIN
|
||||
? 'iam.idp.write'
|
||||
: serviceType === PolicyComponentServiceType.MGMT
|
||||
? 'org.idp.write'
|
||||
: ''
|
||||
]
|
||||
| hasRole
|
||||
| async) === false
|
||||
"
|
||||
mat-icon-button
|
||||
matTooltip="{{ 'IDP.SETUNAVAILABLE' | translate }}"
|
||||
(click)="removeIdp(idp)"
|
||||
>
|
||||
<i class="las la-times-circle"></i>
|
||||
</button>
|
||||
<button actions
|
||||
[disabled]="serviceType === PolicyComponentServiceType.MGMT && idp?.providerType === IDPOwnerType.IDP_OWNER_TYPE_ORG"
|
||||
mat-icon-button color="warn" matTooltip="{{'ACTIONS.REMOVE' | translate}}" (click)="deleteIdp(idp)">
|
||||
<button
|
||||
actions
|
||||
[disabled]="
|
||||
serviceType === PolicyComponentServiceType.MGMT && idp?.providerType === IDPOwnerType.IDP_OWNER_TYPE_ORG
|
||||
"
|
||||
mat-icon-button
|
||||
color="warn"
|
||||
matTooltip="{{ 'ACTIONS.REMOVE' | translate }}"
|
||||
(click)="deleteIdp(idp)"
|
||||
>
|
||||
<i class="las la-trash"></i>
|
||||
</button>
|
||||
</cnsl-table-actions>
|
||||
@@ -119,11 +194,16 @@
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr class="highlight" mat-row *matRowDef="let row; columns: displayedColumns;">
|
||||
</tr>
|
||||
<tr class="highlight" mat-row *matRowDef="let row; columns: displayedColumns"></tr>
|
||||
</table>
|
||||
</div>
|
||||
<cnsl-paginator #paginator class="paginator" [timestamp]="idpResult?.details?.viewTimestamp"
|
||||
[length]="idpResult?.details?.totalResult || 0" [pageSize]="10" [pageSizeOptions]="[10, 20, 50, 100]"
|
||||
(page)="changePage($event)"></cnsl-paginator>
|
||||
</cnsl-refresh-table>
|
||||
<cnsl-paginator
|
||||
#paginator
|
||||
class="paginator"
|
||||
[timestamp]="idpResult?.details?.viewTimestamp"
|
||||
[length]="idpResult?.details?.totalResult || 0"
|
||||
[pageSize]="10"
|
||||
[pageSizeOptions]="[10, 20, 50, 100]"
|
||||
(page)="changePage($event)"
|
||||
></cnsl-paginator>
|
||||
</cnsl-refresh-table>
|
||||
|
@@ -10,7 +10,6 @@ import { RouterModule } from '@angular/router';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
|
||||
import { RefreshTableModule } from 'src/app/modules/refresh-table/refresh-table.module';
|
||||
import { HasFeaturePipeModule } from 'src/app/pipes/has-feature-pipe/has-feature-pipe.module';
|
||||
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module';
|
||||
import { LocalizedDatePipeModule } from 'src/app/pipes/localized-date-pipe/localized-date-pipe.module';
|
||||
import { TimestampToDatePipeModule } from 'src/app/pipes/timestamp-to-date-pipe/timestamp-to-date-pipe.module';
|
||||
@@ -40,7 +39,6 @@ import { IdpTableComponent } from './idp-table.component';
|
||||
RefreshTableModule,
|
||||
HasRoleModule,
|
||||
HasRolePipeModule,
|
||||
HasFeaturePipeModule,
|
||||
TruncatePipeModule,
|
||||
],
|
||||
exports: [IdpTableComponent],
|
||||
|
@@ -1,31 +1,33 @@
|
||||
<h1 class="title" mat-dialog-title>{{'KEYBOARDSHORTCUTS.TITLE' | translate}}</h1>
|
||||
<h1 class="title" mat-dialog-title>{{ 'KEYBOARDSHORTCUTS.TITLE' | translate }}</h1>
|
||||
<div mat-dialog-content>
|
||||
|
||||
<div *ngIf="isNotOnSystem" class="keyboard-shortcuts-group">
|
||||
<h2>{{'KEYBOARDSHORTCUTS.UNDERORGCONTEXT' | translate}}</h2>
|
||||
<h2>{{ 'KEYBOARDSHORTCUTS.UNDERORGCONTEXT' | translate }}</h2>
|
||||
<div class="keyboard-shortcuts-wrapper">
|
||||
<div class="keyboard-shortcut" *ngFor="let shortcut of orgShortcuts">
|
||||
<span class="keyboard-shortcut-name cnsl-secondary-text" [innerHTML]="shortcut.i18nKey | translate"></span>
|
||||
<span class="fill-space"></span>
|
||||
<template *ngFor="let key of shortcut.keyboardKeys; index as i" [ngTemplateOutlet]="actionkey"
|
||||
[ngTemplateOutletContext]="{key: key, last: i === shortcut.keyboardKeys.length-1}"></template>
|
||||
<template
|
||||
*ngFor="let key of shortcut.keyboardKeys; index as i"
|
||||
[ngTemplateOutlet]="actionkey"
|
||||
[ngTemplateOutletContext]="{ key: key, last: i === shortcut.keyboardKeys.length - 1 }"
|
||||
></template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="keyboard-shortcuts-group">
|
||||
<h2>{{'KEYBOARDSHORTCUTS.SIDEWIDE' | translate}}</h2>
|
||||
<h2>{{ 'KEYBOARDSHORTCUTS.SIDEWIDE' | translate }}</h2>
|
||||
<div class="keyboard-shortcuts-wrapper">
|
||||
<ng-container *ngFor="let shortcut of shortcuts">
|
||||
<ng-template cnslHasRole [hasRole]="shortcut.permissions">
|
||||
<ng-template cnslHasFeature [hasFeature]="shortcut.features">
|
||||
<div class="keyboard-shortcut">
|
||||
<span class="keyboard-shortcut-name cnsl-secondary-text"
|
||||
[innerHTML]="shortcut.i18nKey | translate"></span>
|
||||
<span class="fill-space"></span>
|
||||
<template *ngFor="let key of shortcut.keyboardKeys; index as i" [ngTemplateOutlet]="actionkey"
|
||||
[ngTemplateOutletContext]="{key: key, last: i === shortcut.keyboardKeys.length-1}"></template>
|
||||
</div>
|
||||
</ng-template>
|
||||
<div class="keyboard-shortcut">
|
||||
<span class="keyboard-shortcut-name cnsl-secondary-text" [innerHTML]="shortcut.i18nKey | translate"></span>
|
||||
<span class="fill-space"></span>
|
||||
<template
|
||||
*ngFor="let key of shortcut.keyboardKeys; index as i"
|
||||
[ngTemplateOutlet]="actionkey"
|
||||
[ngTemplateOutletContext]="{ key: key, last: i === shortcut.keyboardKeys.length - 1 }"
|
||||
></template>
|
||||
</div>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
</div>
|
||||
@@ -33,7 +35,7 @@
|
||||
</div>
|
||||
<div mat-dialog-actions class="action">
|
||||
<button mat-stroked-button (click)="closeDialog()">
|
||||
{{'ACTIONS.CLOSE' | translate}}
|
||||
{{ 'ACTIONS.CLOSE' | translate }}
|
||||
</button>
|
||||
<span class="fill-space"></span>
|
||||
</div>
|
||||
@@ -41,7 +43,7 @@
|
||||
<ng-template #actionkey let-key="key" let-last="last">
|
||||
<div class="keyboard-shortcuts-action-key">
|
||||
<div class="keyboard-shortcuts-key-overlay"></div>
|
||||
<span class="keyboard-shortcuts-span">{{key}}</span>
|
||||
<span class="keyboard-shortcuts-span">{{ key }}</span>
|
||||
</div>
|
||||
<!-- <span *ngIf="!last" class="keyboard-shortcuts-plus cnsl-secondary-text">+</span> -->
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
|
@@ -4,13 +4,12 @@ import { FormsModule } from '@angular/forms';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { HasFeatureModule } from 'src/app/directives/has-feature/has-feature.module';
|
||||
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
|
||||
|
||||
import { KeyboardShortcutsComponent } from './keyboard-shortcuts.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [KeyboardShortcutsComponent],
|
||||
imports: [CommonModule, FormsModule, TranslateModule, HasFeatureModule, HasRoleModule, MatButtonModule, MatIconModule],
|
||||
imports: [CommonModule, FormsModule, TranslateModule, HasRoleModule, MatButtonModule, MatIconModule],
|
||||
})
|
||||
export class KeyboardShortcutsModule {}
|
||||
|
@@ -1,37 +1,52 @@
|
||||
<ng-container *ngIf="((['iam.read$','iam.write$'] | hasRole)) as iamuser$">
|
||||
<div class="nav-col" [ngClass]="{'is-admin': (iamuser$ | async)}">
|
||||
|
||||
<ng-container *ngIf="['iam.read$', 'iam.write$'] | hasRole as iamuser$">
|
||||
<div class="nav-col" [ngClass]="{ 'is-admin': (iamuser$ | async) }">
|
||||
<ng-container
|
||||
*ngIf="breadcrumbService.breadcrumbsExtended$ && (breadcrumbService.breadcrumbsExtended$ | async) as breadc">
|
||||
|
||||
*ngIf="breadcrumbService.breadcrumbsExtended$ && (breadcrumbService.breadcrumbsExtended$ | async) as breadc"
|
||||
>
|
||||
<ng-container
|
||||
*ngIf="breadc[breadc.length - 1] && !breadc[breadc.length - 1].hideNav && breadc[breadc.length - 1].type !== BreadcrumbType.GRANTEDPROJECT && breadc[breadc.length - 1].type !== BreadcrumbType.APP && breadc[breadc.length - 1].type !== BreadcrumbType.AUTHUSER"
|
||||
[ngSwitch]="breadc[breadc.length - 1].type">
|
||||
*ngIf="
|
||||
breadc[breadc.length - 1] &&
|
||||
!breadc[breadc.length - 1].hideNav &&
|
||||
breadc[breadc.length - 1].type !== BreadcrumbType.GRANTEDPROJECT &&
|
||||
breadc[breadc.length - 1].type !== BreadcrumbType.APP &&
|
||||
breadc[breadc.length - 1].type !== BreadcrumbType.AUTHUSER
|
||||
"
|
||||
[ngSwitch]="breadc[breadc.length - 1].type"
|
||||
>
|
||||
<div class="nav-row" @navrow>
|
||||
<ng-container *ngSwitchCase="BreadcrumbType.IAM">
|
||||
<div class="nav-row-abs" @navroworg>
|
||||
<ng-template cnslHasRole [hasRole]="['iam.read']">
|
||||
<a class="nav-item" [routerLinkActiveOptions]="{ exact: false }" [routerLinkActive]="['active']"
|
||||
[routerLink]="[ '/system' ]">
|
||||
|
||||
<a
|
||||
class="nav-item"
|
||||
[routerLinkActiveOptions]="{ exact: false }"
|
||||
[routerLinkActive]="['active']"
|
||||
[routerLink]="['/system']"
|
||||
>
|
||||
<div class="c_label">
|
||||
<span> {{'MENU.IAM' | translate}} </span>
|
||||
<span> {{ 'MENU.INSTANCEOVERVIEW' | translate }} </span>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<a class="nav-item" [routerLinkActiveOptions]="{ exact: false }" [routerLinkActive]="['active']"
|
||||
[routerLink]="[ '/views']">
|
||||
|
||||
<a
|
||||
class="nav-item"
|
||||
[routerLinkActiveOptions]="{ exact: false }"
|
||||
[routerLinkActive]="['active']"
|
||||
[routerLink]="['/views']"
|
||||
>
|
||||
<div class="c_label">
|
||||
<span> {{'MENU.VIEWS' | translate}} </span>
|
||||
<span> {{ 'MENU.VIEWS' | translate }} </span>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<a class="nav-item" [routerLinkActiveOptions]="{ exact: false }" [routerLinkActive]="['active']"
|
||||
[routerLink]="[ '/failed-events']">
|
||||
|
||||
<a
|
||||
class="nav-item"
|
||||
[routerLinkActiveOptions]="{ exact: false }"
|
||||
[routerLinkActive]="['active']"
|
||||
[routerLink]="['/failed-events']"
|
||||
>
|
||||
<div class="c_label">
|
||||
<span> {{'MENU.FAILEDEVENTS' | translate}} </span>
|
||||
<span> {{ 'MENU.FAILEDEVENTS' | translate }} </span>
|
||||
</div>
|
||||
</a>
|
||||
</ng-template>
|
||||
@@ -42,60 +57,89 @@
|
||||
|
||||
<ng-container *ngSwitchCase="BreadcrumbType.ORG">
|
||||
<div class="nav-row-abs" @navroworg>
|
||||
<a class="nav-item" [routerLinkActive]="['active']" [routerLinkActiveOptions]="{ exact: true }"
|
||||
[routerLink]="[ '/']">
|
||||
<span class="label">{{'MENU.DASHBOARD' | translate}}</span>
|
||||
<a
|
||||
class="nav-item"
|
||||
[routerLinkActive]="['active']"
|
||||
[routerLinkActiveOptions]="{ exact: true }"
|
||||
[routerLink]="['/']"
|
||||
>
|
||||
<span class="label">{{ 'MENU.DASHBOARD' | translate }}</span>
|
||||
</a>
|
||||
|
||||
<ng-container class="org-list" *ngIf="org" [@navAnimation]="org">
|
||||
<ng-template cnslHasRole [hasRole]="['org.read']">
|
||||
<a class="nav-item" [routerLinkActive]="['active']" [routerLinkActiveOptions]="{ exact: false }"
|
||||
[routerLink]="[ '/org']">
|
||||
<span class="label">{{'MENU.ORGANIZATION' | translate}}</span>
|
||||
<a
|
||||
class="nav-item"
|
||||
[routerLinkActive]="['active']"
|
||||
[routerLinkActiveOptions]="{ exact: false }"
|
||||
[routerLink]="['/org']"
|
||||
>
|
||||
<span class="label">{{ 'MENU.ORGANIZATION' | translate }}</span>
|
||||
</a>
|
||||
</ng-template>
|
||||
|
||||
<ng-template cnslHasRole [hasRole]="['project.read(:[0-9]*)?']">
|
||||
<a class="nav-item" [routerLinkActive]="['active']" [routerLinkActiveOptions]="{ exact: false }"
|
||||
[routerLink]="[ '/projects']">
|
||||
|
||||
<a
|
||||
class="nav-item"
|
||||
[routerLinkActive]="['active']"
|
||||
[routerLinkActiveOptions]="{ exact: false }"
|
||||
[routerLink]="['/projects']"
|
||||
>
|
||||
<div class="c_label">
|
||||
<span> {{'MENU.PROJECT' | translate}} </span>
|
||||
<span> {{ 'MENU.PROJECT' | translate }} </span>
|
||||
<small
|
||||
*ngIf="(mgmtService?.ownedProjectsCount | async) && (mgmtService?.grantedProjectsCount | async)"
|
||||
class="count">({{((mgmtService?.ownedProjectsCount | async) ?? 0) +
|
||||
((mgmtService?.grantedProjectsCount | async) ?? 0)}})</small>
|
||||
class="count"
|
||||
>({{
|
||||
((mgmtService?.ownedProjectsCount | async) ?? 0) +
|
||||
((mgmtService?.grantedProjectsCount | async) ?? 0)
|
||||
}})</small
|
||||
>
|
||||
</div>
|
||||
</a>
|
||||
</ng-template>
|
||||
|
||||
<ng-template cnslHasRole [hasRole]="['user.read(:[0-9]*)?']">
|
||||
<a class="nav-item" [routerLinkActive]="['active']" [routerLink]="[ '/users']"
|
||||
[routerLinkActiveOptions]="{ exact: false }">
|
||||
<a
|
||||
class="nav-item"
|
||||
[routerLinkActive]="['active']"
|
||||
[routerLink]="['/users']"
|
||||
[routerLinkActiveOptions]="{ exact: false }"
|
||||
>
|
||||
<span class="label">{{ 'MENU.HUMANUSERS' | translate }}</span>
|
||||
</a>
|
||||
</ng-template>
|
||||
|
||||
<ng-template cnslHasRole [hasRole]="['user.grant.read(:[0-9]*)?']">
|
||||
<a class="nav-item" [routerLinkActive]="['active']" [routerLink]="[ '/grants']"
|
||||
[routerLinkActiveOptions]="{ exact: true }">
|
||||
<a
|
||||
class="nav-item"
|
||||
[routerLinkActive]="['active']"
|
||||
[routerLink]="['/grants']"
|
||||
[routerLinkActiveOptions]="{ exact: true }"
|
||||
>
|
||||
<span class="label">{{ 'MENU.GRANTS' | translate }}</span>
|
||||
</a>
|
||||
</ng-template>
|
||||
|
||||
<ng-template cnslHasFeature [hasFeature]="['actions']">
|
||||
<ng-template cnslHasRole [hasRole]="['org.action.read']">
|
||||
<a class="nav-item" [routerLinkActive]="['active']" [routerLink]="[ '/actions']"
|
||||
[routerLinkActiveOptions]="{ exact: true }">
|
||||
<span class="label">{{ 'MENU.ACTIONS' | translate }}</span>
|
||||
</a>
|
||||
</ng-template>
|
||||
<ng-template cnslHasRole [hasRole]="['org.action.read']">
|
||||
<a
|
||||
class="nav-item"
|
||||
[routerLinkActive]="['active']"
|
||||
[routerLink]="['/actions']"
|
||||
[routerLinkActiveOptions]="{ exact: true }"
|
||||
>
|
||||
<span class="label">{{ 'MENU.ACTIONS' | translate }}</span>
|
||||
</a>
|
||||
</ng-template>
|
||||
|
||||
<ng-template cnslHasRole [hasRole]="['org.read']">
|
||||
<a class="nav-item" [routerLinkActive]="['active']" [routerLink]="[ '/domains']"
|
||||
[routerLinkActiveOptions]="{ exact: true }">
|
||||
<span class="label">{{'MENU.DOMAINS' | translate}}</span>
|
||||
<a
|
||||
class="nav-item"
|
||||
[routerLinkActive]="['active']"
|
||||
[routerLink]="['/domains']"
|
||||
[routerLinkActiveOptions]="{ exact: true }"
|
||||
>
|
||||
<span class="label">{{ 'MENU.DOMAINS' | translate }}</span>
|
||||
</a>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
@@ -107,44 +151,56 @@
|
||||
<ng-container *ngSwitchCase="BreadcrumbType.PROJECT">
|
||||
<div *ngIf="breadc[breadc.length - 1]?.param?.value" class="nav-row-abs" @navrowproject>
|
||||
<ng-template cnslHasRole [hasRole]="['project.read(:[0-9]*)?']">
|
||||
<a class="nav-item" [routerLinkActiveOptions]="{ exact: true }" [routerLinkActive]="['active']"
|
||||
[routerLink]="[ '/projects', breadc[breadc.length - 1]?.param?.value ]">
|
||||
|
||||
<a
|
||||
class="nav-item"
|
||||
[routerLinkActiveOptions]="{ exact: true }"
|
||||
[routerLinkActive]="['active']"
|
||||
[routerLink]="['/projects', breadc[breadc.length - 1]?.param?.value]"
|
||||
>
|
||||
<div class="c_label">
|
||||
<span> {{'MENU.PROJECTOVERVIEW' | translate}} </span>
|
||||
<span> {{ 'MENU.PROJECTOVERVIEW' | translate }} </span>
|
||||
</div>
|
||||
</a>
|
||||
</ng-template>
|
||||
|
||||
<ng-template cnslHasRole [hasRole]="['project.read(:[0-9]*)?']">
|
||||
<a *ngIf="!breadc[breadc.length - 1]?.isZitadel" class="nav-item"
|
||||
[routerLinkActiveOptions]="{ exact: false }" [routerLinkActive]="['active']"
|
||||
[routerLink]="[ '/projects', breadc[breadc.length - 1]?.param?.value, 'roles']">
|
||||
|
||||
<a
|
||||
*ngIf="!breadc[breadc.length - 1]?.isZitadel"
|
||||
class="nav-item"
|
||||
[routerLinkActiveOptions]="{ exact: false }"
|
||||
[routerLinkActive]="['active']"
|
||||
[routerLink]="['/projects', breadc[breadc.length - 1]?.param?.value, 'roles']"
|
||||
>
|
||||
<div class="c_label">
|
||||
<span> {{'MENU.ROLES' | translate}} </span>
|
||||
<span> {{ 'MENU.ROLES' | translate }} </span>
|
||||
</div>
|
||||
</a>
|
||||
</ng-template>
|
||||
|
||||
<ng-template cnslHasRole [hasRole]="['project.read(:[0-9]*)?']">
|
||||
<a *ngIf="!breadc[breadc.length - 1]?.isZitadel" class="nav-item"
|
||||
[routerLinkActiveOptions]="{ exact: false }" [routerLinkActive]="['active']"
|
||||
[routerLink]="[ '/projects', breadc[breadc.length - 1]?.param?.value, 'projectgrants' ]">
|
||||
|
||||
<a
|
||||
*ngIf="!breadc[breadc.length - 1]?.isZitadel"
|
||||
class="nav-item"
|
||||
[routerLinkActiveOptions]="{ exact: false }"
|
||||
[routerLinkActive]="['active']"
|
||||
[routerLink]="['/projects', breadc[breadc.length - 1]?.param?.value, 'projectgrants']"
|
||||
>
|
||||
<div class="c_label">
|
||||
<span> {{'MENU.PROJECTGRANTS' | translate}} </span>
|
||||
<span> {{ 'MENU.PROJECTGRANTS' | translate }} </span>
|
||||
</div>
|
||||
</a>
|
||||
</ng-template>
|
||||
|
||||
<ng-template cnslHasRole [hasRole]="['project.read(:[0-9]*)?']">
|
||||
<a *ngIf="!breadc[breadc.length - 1]?.isZitadel" class="nav-item"
|
||||
[routerLinkActiveOptions]="{ exact: true }" [routerLinkActive]="['active']"
|
||||
[routerLink]="[ '/projects', breadc[breadc.length - 1]?.param?.value, 'grants' ]">
|
||||
|
||||
<a
|
||||
*ngIf="!breadc[breadc.length - 1]?.isZitadel"
|
||||
class="nav-item"
|
||||
[routerLinkActiveOptions]="{ exact: true }"
|
||||
[routerLinkActive]="['active']"
|
||||
[routerLink]="['/projects', breadc[breadc.length - 1]?.param?.value, 'grants']"
|
||||
>
|
||||
<div class="c_label">
|
||||
<span> {{'MENU.GRANTS' | translate}} </span>
|
||||
<span> {{ 'MENU.GRANTS' | translate }} </span>
|
||||
</div>
|
||||
</a>
|
||||
</ng-template>
|
||||
@@ -156,15 +212,14 @@
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
</ng-container>
|
||||
|
||||
<ng-template #shortcutKeyRef>
|
||||
<ng-container *ngIf="(isHandset$ | async) === false">
|
||||
<span class="fill-space"></span>
|
||||
<div class="nav-shortcut-action-key" matTooltip="{{'MENU.OPENSHORTCUTSTOOLTIP' | translate}}">
|
||||
<div class="nav-shortcut-action-key" matTooltip="{{ 'MENU.OPENSHORTCUTSTOOLTIP' | translate }}">
|
||||
<div class="nav-key-overlay"></div>
|
||||
<span>?</span>
|
||||
</div>
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
|
@@ -9,9 +9,7 @@ import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { HasFeatureModule } from 'src/app/directives/has-feature/has-feature.module';
|
||||
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
|
||||
import { HasFeaturePipeModule } from 'src/app/pipes/has-feature-pipe/has-feature-pipe.module';
|
||||
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module';
|
||||
|
||||
import { SharedModule } from '../shared/shared.module';
|
||||
@@ -28,8 +26,6 @@ import { NavComponent } from './nav.component';
|
||||
RouterModule,
|
||||
MatTooltipModule,
|
||||
HasRolePipeModule,
|
||||
HasFeaturePipeModule,
|
||||
HasFeatureModule,
|
||||
HasRoleModule,
|
||||
MatMenuModule,
|
||||
MatButtonModule,
|
||||
|
@@ -1,190 +1,236 @@
|
||||
<cnsl-detail-layout [hasBackButton]="true" [title]="'POLICY.LOGIN_POLICY.TITLE' | translate"
|
||||
[description]="(serviceType === PolicyComponentServiceType.MGMT ? 'POLICY.LOGIN_POLICY.DESCRIPTIONCREATEMGMT' : PolicyComponentServiceType.ADMIN ? 'POLICY.LOGIN_POLICY.DESCRIPTIONCREATEADMIN' : '') | translate">
|
||||
<p class="policy-applied-to" sub>{{'POLICY.APPLIEDTO' | translate}}:
|
||||
<strong *ngIf="orgName; else iam">{{orgName}}</strong>
|
||||
<ng-template #iam><strong>{{'MENU.SYSTEM' | translate}}</strong>
|
||||
</ng-template>
|
||||
</p>
|
||||
<cnsl-detail-layout
|
||||
[hasBackButton]="true"
|
||||
[title]="'POLICY.LOGIN_POLICY.TITLE' | translate"
|
||||
[description]="
|
||||
(serviceType === PolicyComponentServiceType.MGMT
|
||||
? 'POLICY.LOGIN_POLICY.DESCRIPTIONCREATEMGMT'
|
||||
: PolicyComponentServiceType.ADMIN
|
||||
? 'POLICY.LOGIN_POLICY.DESCRIPTIONCREATEADMIN'
|
||||
: ''
|
||||
) | translate
|
||||
"
|
||||
>
|
||||
<p class="policy-applied-to" sub>
|
||||
{{ 'POLICY.APPLIEDTO' | translate }}:
|
||||
<strong *ngIf="orgName; else iam">{{ orgName }}</strong>
|
||||
<ng-template #iam
|
||||
><strong>{{ 'MENU.INSTANCE' | translate }}</strong>
|
||||
</ng-template>
|
||||
</p>
|
||||
|
||||
<cnsl-info-section *ngIf="isDefault"> {{'POLICY.DEFAULTLABEL' | translate}}</cnsl-info-section>
|
||||
<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 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 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>
|
||||
|
||||
<ng-template cnslHasRole [hasRole]="['org.idp.read']">
|
||||
<cnsl-card title="{{ 'IDP.LIST.TITLE' | translate }}" description="{{ 'IDP.LIST.DESCRIPTION' | translate }}">
|
||||
|
||||
<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>
|
||||
|
||||
<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-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>
|
||||
|
||||
<cnsl-card title="{{ 'MFA.LIST.MULTIFACTORTITLE' | translate }}"
|
||||
description="{{'MFA.LIST.MULTIFACTORDESCRIPTION' | translate}}" [expanded]="true">
|
||||
<div class="login-policy-row">
|
||||
<cnsl-form-field *ngIf="loginData" class="passwordless-allowed" 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>
|
||||
<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-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>
|
||||
|
||||
<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]="(loginData?.passwordlessType === PasswordlessType.PASSWORDLESS_TYPE_NOT_ALLOWED) || (([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>
|
||||
<ng-template cnslHasRole [hasRole]="['org.idp.read']">
|
||||
<cnsl-card title="{{ 'IDP.LIST.TITLE' | translate }}" description="{{ 'IDP.LIST.DESCRIPTION' | translate }}">
|
||||
<cnsl-idp-table
|
||||
[service]="service"
|
||||
[serviceType]="serviceType"
|
||||
[disabled]="
|
||||
([
|
||||
serviceType === PolicyComponentServiceType.ADMIN
|
||||
? 'iam.idp.write'
|
||||
: serviceType === PolicyComponentServiceType.MGMT
|
||||
? 'org.idp.write'
|
||||
: ''
|
||||
]
|
||||
| hasRole
|
||||
| async) === false
|
||||
"
|
||||
>
|
||||
</cnsl-idp-table>
|
||||
</cnsl-card>
|
||||
</ng-template>
|
||||
|
||||
<cnsl-card *ngIf="loginData" title="{{ 'MFA.LIST.SECONDFACTORTITLE' | translate }}"
|
||||
description="{{'MFA.LIST.SECONDFACTORDESCRIPTION' | translate}}" [expanded]="true">
|
||||
<mat-slide-toggle card-actions class="force-mfa-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"
|
||||
[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 }}">
|
||||
<div class="login-policy-content" *ngIf="loginData">
|
||||
<div class="login-policy-row">
|
||||
<mat-slide-toggle color="primary" matTooltip="{{'POLICY.DATA.FORCEMFA_DESC' | translate}}"
|
||||
[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="login-policy-row">
|
||||
<mat-slide-toggle class="login-policy-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="login-policy-row">
|
||||
<mat-slide-toggle class="login-policy-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>
|
||||
|
||||
<ng-template #idpInfo>
|
||||
<cnsl-info-section class="info">
|
||||
{{'POLICY.DATA.ALLOWEXTERNALIDP_DESC' | translate}}
|
||||
</cnsl-info-section>
|
||||
</ng-template>
|
||||
</div>
|
||||
|
||||
<div class="login-policy-row">
|
||||
<mat-slide-toggle class="login-policy-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]="InfoSectionType.WARN">
|
||||
<span
|
||||
[innerHTML]="'FEATURES.NOTAVAILABLE' | translate: ({value: 'login_policy.password_reset'})"></span>
|
||||
</cnsl-info-section>
|
||||
|
||||
<ng-template #passwordResetInfo>
|
||||
<cnsl-info-section class="info">
|
||||
{{'POLICY.DATA.HIDEPASSWORDRESET_DESC' | translate}}
|
||||
</cnsl-info-section>
|
||||
</ng-template>
|
||||
</div>
|
||||
</div>
|
||||
</cnsl-card>
|
||||
|
||||
<div class="login-policy-btn-container">
|
||||
<button [disabled]="disabled" class="login-policy-save-button" (click)="savePolicy()" color="primary"
|
||||
type="submit" mat-raised-button>{{ 'ACTIONS.SAVE' | translate }}</button>
|
||||
<cnsl-card
|
||||
title="{{ 'MFA.LIST.MULTIFACTORTITLE' | translate }}"
|
||||
description="{{ 'MFA.LIST.MULTIFACTORDESCRIPTION' | translate }}"
|
||||
[expanded]="true"
|
||||
>
|
||||
<div class="login-policy-row">
|
||||
<cnsl-form-field *ngIf="loginData" class="passwordless-allowed" label="Access Code" required="true">
|
||||
<cnsl-label>{{ 'LOGINPOLICY.PASSWORDLESS' | translate }}</cnsl-label>
|
||||
<mat-select [(ngModel)]="loginData.passwordlessType" [disabled]="disabled">
|
||||
<mat-option *ngFor="let pt of passwordlessTypes" [value]="pt">
|
||||
{{ 'LOGINPOLICY.PASSWORDLESSTYPE.' + pt | translate }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</cnsl-form-field>
|
||||
</div>
|
||||
|
||||
<cnsl-policy-grid class="grid" [currentPolicy]="currentPolicy" [type]="serviceType" tagForFilter="security">
|
||||
</cnsl-policy-grid>
|
||||
</cnsl-detail-layout>
|
||||
<cnsl-mfa-table
|
||||
[service]="service"
|
||||
[serviceType]="serviceType"
|
||||
[componentType]="LoginMethodComponentType.MultiFactor"
|
||||
[disabled]="
|
||||
loginData?.passwordlessType === PasswordlessType.PASSWORDLESS_TYPE_NOT_ALLOWED ||
|
||||
([
|
||||
serviceType === PolicyComponentServiceType.ADMIN
|
||||
? 'iam.policy.write'
|
||||
: serviceType === PolicyComponentServiceType.MGMT
|
||||
? 'policy.write'
|
||||
: ''
|
||||
]
|
||||
| hasRole
|
||||
| async) === false
|
||||
"
|
||||
>
|
||||
</cnsl-mfa-table>
|
||||
</cnsl-card>
|
||||
|
||||
<cnsl-card
|
||||
*ngIf="loginData"
|
||||
title="{{ 'MFA.LIST.SECONDFACTORTITLE' | translate }}"
|
||||
description="{{ 'MFA.LIST.SECONDFACTORDESCRIPTION' | translate }}"
|
||||
[expanded]="true"
|
||||
>
|
||||
<mat-slide-toggle
|
||||
card-actions
|
||||
class="force-mfa-toggle"
|
||||
color="primary"
|
||||
[disabled]="disabled"
|
||||
ngDefaultControl
|
||||
[(ngModel)]="loginData.forceMfa"
|
||||
>
|
||||
{{ 'POLICY.DATA.FORCEMFA' | translate }}
|
||||
</mat-slide-toggle>
|
||||
|
||||
<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
|
||||
"
|
||||
>
|
||||
</cnsl-mfa-table>
|
||||
</cnsl-card>
|
||||
|
||||
<cnsl-card title="{{ 'POLICY.LOGIN_POLICY.ADVANCED' | translate }}">
|
||||
<div class="login-policy-content" *ngIf="loginData">
|
||||
<div class="login-policy-row">
|
||||
<mat-slide-toggle
|
||||
color="primary"
|
||||
matTooltip="{{ 'POLICY.DATA.FORCEMFA_DESC' | translate }}"
|
||||
[disabled]="disabled"
|
||||
ngDefaultControl
|
||||
[(ngModel)]="loginData.allowUsernamePassword"
|
||||
>
|
||||
{{ 'POLICY.DATA.ALLOWUSERNAMEPASSWORD' | translate }}
|
||||
</mat-slide-toggle>
|
||||
|
||||
<ng-template #usernameInfo>
|
||||
<cnsl-info-section class="info">
|
||||
{{ 'POLICY.DATA.ALLOWUSERNAMEPASSWORD_DESC' | translate }}
|
||||
</cnsl-info-section>
|
||||
</ng-template>
|
||||
</div>
|
||||
<div class="login-policy-row">
|
||||
<mat-slide-toggle
|
||||
class="login-policy-toggle"
|
||||
color="primary"
|
||||
[disabled]="disabled"
|
||||
ngDefaultControl
|
||||
[(ngModel)]="loginData.allowRegister"
|
||||
>
|
||||
{{ 'POLICY.DATA.ALLOWREGISTER' | translate }}
|
||||
</mat-slide-toggle>
|
||||
|
||||
<ng-template #regInfo>
|
||||
<cnsl-info-section class="info">
|
||||
{{ 'POLICY.DATA.ALLOWREGISTER_DESC' | translate }}
|
||||
</cnsl-info-section>
|
||||
</ng-template>
|
||||
</div>
|
||||
<div class="login-policy-row">
|
||||
<mat-slide-toggle
|
||||
class="login-policy-toggle"
|
||||
color="primary"
|
||||
[disabled]="disabled"
|
||||
ngDefaultControl
|
||||
[(ngModel)]="loginData.allowExternalIdp"
|
||||
>
|
||||
{{ 'POLICY.DATA.ALLOWEXTERNALIDP' | translate }}
|
||||
</mat-slide-toggle>
|
||||
|
||||
<ng-template #idpInfo>
|
||||
<cnsl-info-section class="info">
|
||||
{{ 'POLICY.DATA.ALLOWEXTERNALIDP_DESC' | translate }}
|
||||
</cnsl-info-section>
|
||||
</ng-template>
|
||||
</div>
|
||||
|
||||
<div class="login-policy-row">
|
||||
<mat-slide-toggle
|
||||
class="login-policy-toggle"
|
||||
color="primary"
|
||||
[disabled]="disabled"
|
||||
ngDefaultControl
|
||||
[(ngModel)]="loginData.hidePasswordReset"
|
||||
>
|
||||
{{ 'POLICY.DATA.HIDEPASSWORDRESET' | translate }}
|
||||
</mat-slide-toggle>
|
||||
|
||||
<ng-template #passwordResetInfo>
|
||||
<cnsl-info-section class="info">
|
||||
{{ 'POLICY.DATA.HIDEPASSWORDRESET_DESC' | translate }}
|
||||
</cnsl-info-section>
|
||||
</ng-template>
|
||||
</div>
|
||||
</div>
|
||||
</cnsl-card>
|
||||
|
||||
<div class="login-policy-btn-container">
|
||||
<button
|
||||
[disabled]="disabled"
|
||||
class="login-policy-save-button"
|
||||
(click)="savePolicy()"
|
||||
color="primary"
|
||||
type="submit"
|
||||
mat-raised-button
|
||||
>
|
||||
{{ 'ACTIONS.SAVE' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<cnsl-policy-grid class="grid" [currentPolicy]="currentPolicy" [type]="serviceType" tagForFilter="security">
|
||||
</cnsl-policy-grid>
|
||||
</cnsl-detail-layout>
|
||||
|
@@ -15,7 +15,6 @@ import { CardModule } from 'src/app/modules/card/card.module';
|
||||
import { DetailLayoutModule } from 'src/app/modules/detail-layout/detail-layout.module';
|
||||
import { IdpTableModule } from 'src/app/modules/idp-table/idp-table.module';
|
||||
import { InputModule } from 'src/app/modules/input/input.module';
|
||||
import { HasFeaturePipeModule } from 'src/app/pipes/has-feature-pipe/has-feature-pipe.module';
|
||||
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module';
|
||||
|
||||
import { InfoSectionModule } from '../../info-section/info-section.module';
|
||||
@@ -36,7 +35,6 @@ import { MfaTableComponent } from './mfa-table/mfa-table.component';
|
||||
InputModule,
|
||||
MatIconModule,
|
||||
MatButtonModule,
|
||||
HasFeaturePipeModule,
|
||||
MatSlideToggleModule,
|
||||
HasRoleModule,
|
||||
MatDialogModule,
|
||||
|
@@ -1,29 +1,38 @@
|
||||
<cnsl-detail-layout [hasBackButton]="true" [title]="'POLICY.LOGIN_TEXTS.TITLE' | translate"
|
||||
[description]="'POLICY.LOGIN_TEXTS.DESCRIPTION' | translate">
|
||||
|
||||
<p class="policy-applied-to" sub>{{'POLICY.APPLIEDTO' | translate}}:
|
||||
<strong *ngIf="orgName; else iam">{{orgName}}</strong>
|
||||
<ng-template #iam><strong>{{'MENU.SYSTEM' | translate}}</strong>
|
||||
<cnsl-detail-layout
|
||||
[hasBackButton]="true"
|
||||
[title]="'POLICY.LOGIN_TEXTS.TITLE' | translate"
|
||||
[description]="'POLICY.LOGIN_TEXTS.DESCRIPTION' | translate"
|
||||
>
|
||||
<p class="policy-applied-to" sub>
|
||||
{{ 'POLICY.APPLIEDTO' | translate }}:
|
||||
<strong *ngIf="orgName; else iam">{{ orgName }}</strong>
|
||||
<ng-template #iam
|
||||
><strong>{{ 'MENU.INSTANCE' | translate }}</strong>
|
||||
</ng-template>
|
||||
</p>
|
||||
<div class="date">
|
||||
<div>
|
||||
<p class="newer-title" *ngIf="newerVersionExists">{{'POLICY.LOGIN_TEXTS.NEWERVERSIONEXISTS' | translate}}</p>
|
||||
<p *ngIf="newerPolicyChangeDate && newerVersionExists">{{'POLICY.LOGIN_TEXTS.CHANGEDATE' | translate}}:
|
||||
{{newerPolicyChangeDate | timestampToDate | localizedDate: 'dd. MMMM YYYY, HH:mm:ss'}}</p>
|
||||
<p class="cnsl-secondary-text" *ngIf="currentPolicyChangeDate">{{'POLICY.LOGIN_TEXTS.CURRENTDATE' | translate}}:
|
||||
{{currentPolicyChangeDate | timestampToDate | localizedDate: 'dd. MMMM YYYY, HH:mm:ss'}}</p>
|
||||
<p class="newer-title" *ngIf="newerVersionExists">{{ 'POLICY.LOGIN_TEXTS.NEWERVERSIONEXISTS' | translate }}</p>
|
||||
<p *ngIf="newerPolicyChangeDate && newerVersionExists">
|
||||
{{ 'POLICY.LOGIN_TEXTS.CHANGEDATE' | translate }}:
|
||||
{{ newerPolicyChangeDate | timestampToDate | localizedDate: 'dd. MMMM YYYY, HH:mm:ss' }}
|
||||
</p>
|
||||
<p class="cnsl-secondary-text" *ngIf="currentPolicyChangeDate">
|
||||
{{ 'POLICY.LOGIN_TEXTS.CURRENTDATE' | translate }}:
|
||||
{{ currentPolicyChangeDate | timestampToDate | localizedDate: 'dd. MMMM YYYY, HH:mm:ss' }}
|
||||
</p>
|
||||
</div>
|
||||
<button [disabled]="!newerVersionExists" color="primary" mat-raised-button (click)="loadData()">
|
||||
<i class="las la-sync-alt"></i>
|
||||
{{'ACTIONS.REFRESH' | translate}}</button>
|
||||
{{ 'ACTIONS.REFRESH' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
<form *ngIf="form" class="top-actions" [formGroup]="form">
|
||||
<cnsl-form-field class="keys" appearance="outline">
|
||||
<cnsl-label>{{ 'POLICY.LOGIN_TEXTS.KEYNAME' | translate }}</cnsl-label>
|
||||
<mat-select formControlName="currentSubMap" name="currentSubMap">
|
||||
<mat-option *ngFor="let key of KeyNamesArray" [value]="key">
|
||||
{{'POLICY.LOGIN_TEXTS.KEYS.'+key | translate }}
|
||||
{{ 'POLICY.LOGIN_TEXTS.KEYS.' + key | translate }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</cnsl-form-field>
|
||||
@@ -33,40 +42,53 @@
|
||||
<mat-select formControlName="locale" name="locale">
|
||||
<mat-option *ngFor="let loc of LOCALES" [value]="loc">
|
||||
<div class="centerline">
|
||||
<span>{{loc}} <span class="lighter cnsl-secondary-text">| {{'POLICY.LOGIN_TEXTS.LOCALES.'+loc |
|
||||
translate }}</span></span>
|
||||
<span
|
||||
>{{ loc }}
|
||||
<span class="lighter cnsl-secondary-text"
|
||||
>| {{ 'POLICY.LOGIN_TEXTS.LOCALES.' + loc | translate }}</span
|
||||
></span
|
||||
>
|
||||
</div>
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</cnsl-form-field>
|
||||
</form>
|
||||
|
||||
<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) || (canWrite$ | async) === false"
|
||||
[default$]="getDefaultInitMessageTextMap$" [current$]="getCustomInitMessageTextMap$" (changedValues)="updateCurrentValues(
|
||||
$event)"></cnsl-edit-text>
|
||||
<cnsl-edit-text
|
||||
label="one"
|
||||
[disabled]="(canWrite$ | async) === false"
|
||||
[default$]="getDefaultInitMessageTextMap$"
|
||||
[current$]="getCustomInitMessageTextMap$"
|
||||
(changedValues)="updateCurrentValues($event)"
|
||||
></cnsl-edit-text>
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
<!-- *ngIf="totalCustomPolicy && totalCustomPolicy.isDefault === false" -->
|
||||
<button class="reset-button"
|
||||
[disabled]="(serviceType === PolicyComponentServiceType.MGMT && (['custom_text.login'] | hasFeature | async) === false) || (canWrite$ | 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) || (canWrite$ | async) === false"
|
||||
(click)="saveCurrentMessage()" color="primary" type="submit" mat-raised-button>{{ 'ACTIONS.SAVE' | translate
|
||||
}}</button>
|
||||
<button
|
||||
class="reset-button"
|
||||
[disabled]="(canWrite$ | 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]="(canWrite$ | async) === false"
|
||||
(click)="saveCurrentMessage()"
|
||||
color="primary"
|
||||
type="submit"
|
||||
mat-raised-button
|
||||
>
|
||||
{{ 'ACTIONS.SAVE' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<cnsl-policy-grid [currentPolicy]="currentPolicy" [type]="serviceType" tagForFilter="text"></cnsl-policy-grid>
|
||||
</cnsl-detail-layout>
|
||||
</cnsl-detail-layout>
|
||||
|
@@ -15,7 +15,6 @@ import { TimestampToDatePipeModule } from 'src/app/pipes/timestamp-to-date-pipe/
|
||||
import { HasRoleModule } from '../../../directives/has-role/has-role.module';
|
||||
import { DetailLayoutModule } from '../../../modules/detail-layout/detail-layout.module';
|
||||
import { InputModule } from '../../../modules/input/input.module';
|
||||
import { HasFeaturePipeModule } from '../../../pipes/has-feature-pipe/has-feature-pipe.module';
|
||||
import { HasRolePipeModule } from '../../../pipes/has-role-pipe/has-role-pipe.module';
|
||||
import { EditTextModule } from '../../edit-text/edit-text.module';
|
||||
import { FormFieldModule } from '../../form-field/form-field.module';
|
||||
@@ -38,7 +37,6 @@ import { LoginTextsComponent } from './login-texts.component';
|
||||
FormFieldModule,
|
||||
EditTextModule,
|
||||
MatButtonModule,
|
||||
HasFeaturePipeModule,
|
||||
MatIconModule,
|
||||
HasRoleModule,
|
||||
HasRolePipeModule,
|
||||
@@ -55,4 +53,4 @@ import { LoginTextsComponent } from './login-texts.component';
|
||||
LocalizedDatePipeModule,
|
||||
],
|
||||
})
|
||||
export class LoginTextsPolicyModule { }
|
||||
export class LoginTextsPolicyModule {}
|
||||
|
@@ -1,8 +1,13 @@
|
||||
<cnsl-detail-layout [hasBackButton]="true" [title]="'POLICY.MESSAGE_TEXTS.TITLE' | translate"
|
||||
[description]="'POLICY.MESSAGE_TEXTS.DESCRIPTION' | translate">
|
||||
<p class="policy-applied-to" sub>{{'POLICY.APPLIEDTO' | translate}}:
|
||||
<strong *ngIf="orgName; else iam">{{orgName}}</strong>
|
||||
<ng-template #iam><strong>{{'MENU.SYSTEM' | translate}}</strong>
|
||||
<cnsl-detail-layout
|
||||
[hasBackButton]="true"
|
||||
[title]="'POLICY.MESSAGE_TEXTS.TITLE' | translate"
|
||||
[description]="'POLICY.MESSAGE_TEXTS.DESCRIPTION' | translate"
|
||||
>
|
||||
<p class="policy-applied-to" sub>
|
||||
{{ 'POLICY.APPLIEDTO' | translate }}:
|
||||
<strong *ngIf="orgName; else iam">{{ orgName }}</strong>
|
||||
<ng-template #iam
|
||||
><strong>{{ 'MENU.INSTANCE' | translate }}</strong>
|
||||
</ng-template>
|
||||
</p>
|
||||
|
||||
@@ -10,8 +15,8 @@
|
||||
<cnsl-form-field class="type">
|
||||
<cnsl-label>{{ 'POLICY.MESSAGE_TEXTS.TYPE' | translate }}</cnsl-label>
|
||||
<mat-select [(ngModel)]="currentType" name="currentSubMap" (selectionChange)="changedCurrentType()">
|
||||
<mat-option *ngFor="let type of (MESSAGETYPES | keyvalue)" [value]="type.value">
|
||||
{{'POLICY.MESSAGE_TEXTS.TYPES.'+type.value | translate}}
|
||||
<mat-option *ngFor="let type of MESSAGETYPES | keyvalue" [value]="type.value">
|
||||
{{ 'POLICY.MESSAGE_TEXTS.TYPES.' + type.value | translate }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</cnsl-form-field>
|
||||
@@ -21,38 +26,52 @@
|
||||
<mat-select [(ngModel)]="locale" name="locale" (selectionChange)="changeLocale($event)">
|
||||
<mat-option *ngFor="let loc of LOCALES" [value]="loc">
|
||||
<div class="centerline">
|
||||
<span>{{loc}} <span class="lighter cnsl-secondary-text">| {{'POLICY.LOGIN_TEXTS.LOCALES.'+loc |
|
||||
translate }}</span></span>
|
||||
<span
|
||||
>{{ loc }}
|
||||
<span class="lighter cnsl-secondary-text"
|
||||
>| {{ 'POLICY.LOGIN_TEXTS.LOCALES.' + loc | translate }}</span
|
||||
></span
|
||||
>
|
||||
</div>
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</cnsl-form-field>
|
||||
</div>
|
||||
|
||||
<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) || (canWrite$ | async) === false"
|
||||
label="one" [default$]="getDefaultInitMessageTextMap$" [current$]="getCustomInitMessageTextMap$" (changedValues)="updateCurrentValues(
|
||||
$event)"></cnsl-edit-text>
|
||||
<cnsl-edit-text
|
||||
[chips]="chips[currentType]"
|
||||
[disabled]="(canWrite$ | async) === false"
|
||||
label="one"
|
||||
[default$]="getDefaultInitMessageTextMap$"
|
||||
[current$]="getCustomInitMessageTextMap$"
|
||||
(changedValues)="updateCurrentValues($event)"
|
||||
></cnsl-edit-text>
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
<button class="reset-button"
|
||||
*ngIf="(getCustomInitMessageTextMap$ | async) && ((getCustomInitMessageTextMap$ | async)?.isDefault === false)"
|
||||
[disabled]="(serviceType === PolicyComponentServiceType.MGMT && (['custom_text.message'] | hasFeature | async) === false) || (canWrite$ | 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) || (canWrite$ | async) === false"
|
||||
(click)="saveCurrentMessage()" color="primary" type="submit" mat-raised-button>{{ 'ACTIONS.SAVE' | translate
|
||||
}}</button>
|
||||
<button
|
||||
class="reset-button"
|
||||
*ngIf="(getCustomInitMessageTextMap$ | async) && (getCustomInitMessageTextMap$ | async)?.isDefault === false"
|
||||
[disabled]="(canWrite$ | 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 || (canWrite$ | async) === false"
|
||||
(click)="saveCurrentMessage()"
|
||||
color="primary"
|
||||
type="submit"
|
||||
mat-raised-button
|
||||
>
|
||||
{{ 'ACTIONS.SAVE' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<cnsl-policy-grid [currentPolicy]="currentPolicy" [type]="serviceType" tagForFilter="text"></cnsl-policy-grid>
|
||||
</cnsl-detail-layout>
|
||||
</cnsl-detail-layout>
|
||||
|
@@ -11,7 +11,6 @@ import { TranslateModule } from '@ngx-translate/core';
|
||||
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
|
||||
import { DetailLayoutModule } from 'src/app/modules/detail-layout/detail-layout.module';
|
||||
import { InputModule } from 'src/app/modules/input/input.module';
|
||||
import { HasFeaturePipeModule } from 'src/app/pipes/has-feature-pipe/has-feature-pipe.module';
|
||||
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module';
|
||||
|
||||
import { EditTextModule } from '../../edit-text/edit-text.module';
|
||||
@@ -33,7 +32,6 @@ import { MessageTextsComponent } from './message-texts.component';
|
||||
FormFieldModule,
|
||||
EditTextModule,
|
||||
MatButtonModule,
|
||||
HasFeaturePipeModule,
|
||||
MatIconModule,
|
||||
HasRoleModule,
|
||||
HasRolePipeModule,
|
||||
@@ -47,4 +45,4 @@ import { MessageTextsComponent } from './message-texts.component';
|
||||
TextFieldModule,
|
||||
],
|
||||
})
|
||||
export class MessageTextsPolicyModule { }
|
||||
export class MessageTextsPolicyModule {}
|
||||
|
@@ -1,34 +1,55 @@
|
||||
<cnsl-detail-layout [hasBackButton]="true" [title]="'POLICY.IAM_POLICY.TITLE' | translate"
|
||||
[description]="'POLICY.IAM_POLICY.DESCRIPTION' | translate">
|
||||
<p class="policy-applied-to" sub>{{'POLICY.APPLIEDTO' | translate}}:
|
||||
<strong *ngIf="orgName; else iam">{{orgName}}</strong>
|
||||
<ng-template #iam><strong>{{'MENU.SYSTEM' | translate}}</strong>
|
||||
<cnsl-detail-layout
|
||||
[hasBackButton]="true"
|
||||
[title]="'POLICY.IAM_POLICY.TITLE' | translate"
|
||||
[description]="'POLICY.IAM_POLICY.DESCRIPTION' | translate"
|
||||
>
|
||||
<p class="policy-applied-to" sub>
|
||||
{{ 'POLICY.APPLIEDTO' | translate }}:
|
||||
<strong *ngIf="orgName; else iam">{{ orgName }}</strong>
|
||||
<ng-template #iam
|
||||
><strong>{{ 'MENU.INSTANCE' | translate }}</strong>
|
||||
</ng-template>
|
||||
</p>
|
||||
|
||||
<cnsl-info-section *ngIf="isDefault"> {{'POLICY.DEFAULTLABEL' | translate}}</cnsl-info-section>
|
||||
<cnsl-info-section *ngIf="isDefault"> {{ 'POLICY.DEFAULTLABEL' | translate }}</cnsl-info-section>
|
||||
|
||||
<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}}
|
||||
<button
|
||||
*ngIf="serviceType === PolicyComponentServiceType.MGMT && !isDefault"
|
||||
matTooltip="{{ 'POLICY.RESET' | translate }}"
|
||||
color="warn"
|
||||
(click)="removePolicy()"
|
||||
mat-stroked-button
|
||||
>
|
||||
{{ 'POLICY.RESET' | translate }}
|
||||
</button>
|
||||
</ng-template>
|
||||
|
||||
<div class="content" *ngIf="iamData">
|
||||
<div class="row">
|
||||
<mat-slide-toggle color="primary" name="hasNumber" ngDefaultControl
|
||||
[disabled]="(['iam.policy.write'] | hasRole | async) === false" [(ngModel)]="iamData.userLoginMustBeDomain">
|
||||
{{'POLICY.DATA.USERLOGINMUSTBEDOMAIN' | translate}}
|
||||
<mat-slide-toggle
|
||||
color="primary"
|
||||
name="hasNumber"
|
||||
ngDefaultControl
|
||||
[disabled]="(['iam.policy.write'] | hasRole | async) === false"
|
||||
[(ngModel)]="iamData.userLoginMustBeDomain"
|
||||
>
|
||||
{{ 'POLICY.DATA.USERLOGINMUSTBEDOMAIN' | translate }}
|
||||
</mat-slide-toggle>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="btn-container">
|
||||
<button (click)="savePolicy()" [disabled]="(['iam.policy.write'] | hasRole | async) === false" color="primary"
|
||||
type="submit" mat-raised-button>{{ 'ACTIONS.SAVE' | translate
|
||||
}}</button>
|
||||
<button
|
||||
(click)="savePolicy()"
|
||||
[disabled]="(['iam.policy.write'] | hasRole | async) === false"
|
||||
color="primary"
|
||||
type="submit"
|
||||
mat-raised-button
|
||||
>
|
||||
{{ 'ACTIONS.SAVE' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<cnsl-policy-grid [currentPolicy]="currentPolicy" [type]="serviceType" tagForFilter="login"></cnsl-policy-grid>
|
||||
</cnsl-detail-layout>
|
||||
</cnsl-detail-layout>
|
||||
|
@@ -1,88 +1,113 @@
|
||||
<cnsl-detail-layout [hasBackButton]="true" [title]="'POLICY.PWD_COMPLEXITY.TITLE' | translate"
|
||||
[description]="'POLICY.PWD_COMPLEXITY.DESCRIPTION' | translate">
|
||||
|
||||
<p class="policy-applied-to" sub>{{'POLICY.APPLIEDTO' | translate}}:
|
||||
<strong *ngIf="orgName; else iam">{{orgName}}</strong>
|
||||
<ng-template #iam><strong>{{'MENU.SYSTEM' | translate}}</strong>
|
||||
<cnsl-detail-layout
|
||||
[hasBackButton]="true"
|
||||
[title]="'POLICY.PWD_COMPLEXITY.TITLE' | translate"
|
||||
[description]="'POLICY.PWD_COMPLEXITY.DESCRIPTION' | translate"
|
||||
>
|
||||
<p class="policy-applied-to" sub>
|
||||
{{ 'POLICY.APPLIEDTO' | translate }}:
|
||||
<strong *ngIf="orgName; else iam">{{ orgName }}</strong>
|
||||
<ng-template #iam
|
||||
><strong>{{ 'MENU.INSTANCE' | translate }}</strong>
|
||||
</ng-template>
|
||||
</p>
|
||||
<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]="InfoSectionType.WARN">
|
||||
<span [innerHTML]="'FEATURES.NOTAVAILABLE' | translate: ({value: 'password_complexity_policy'})"></span>
|
||||
</cnsl-info-section>
|
||||
<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>
|
||||
|
||||
<ng-template cnslHasRole [hasRole]="['policy.delete']">
|
||||
<button *ngIf="serviceType === PolicyComponentServiceType.MGMT && !isDefault"
|
||||
[disabled]="serviceType === PolicyComponentServiceType.MGMT && ((['password_complexity_policy'] | hasFeature | async) === false || (['policy.write'] | hasRole | async) === false)"
|
||||
matTooltip="{{'POLICY.RESET' | translate}}" color="warn" (click)="removePolicy()" mat-stroked-button>
|
||||
{{'POLICY.RESET' | translate}}
|
||||
<button
|
||||
*ngIf="serviceType === PolicyComponentServiceType.MGMT && !isDefault"
|
||||
matTooltip="{{ 'POLICY.RESET' | translate }}"
|
||||
color="warn"
|
||||
(click)="removePolicy()"
|
||||
mat-stroked-button
|
||||
>
|
||||
{{ 'POLICY.RESET' | translate }}
|
||||
</button>
|
||||
</ng-template>
|
||||
|
||||
<div *ngIf="complexityData" class="complexity-content">
|
||||
<div class="row">
|
||||
<mat-icon class="icon" svgIcon="mdi_counter"></mat-icon>
|
||||
<span class="left-desc">{{'POLICY.DATA.MINLENGTH' | translate}}</span>
|
||||
<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 || (['policy.write'] | hasRole | async) === false) || (serviceType === PolicyComponentServiceType.ADMIN && (['policy.write'] | hasRole | async) === false)">
|
||||
<button mat-icon-button (click)="decrementLength()" [disabled]="(['policy.write'] | hasRole | 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 || (['policy.write'] | hasRole | async) === false) || (serviceType === PolicyComponentServiceType.ADMIN && (['policy.write'] | hasRole | async) === false)">
|
||||
<span>{{ complexityData?.minLength }}</span>
|
||||
<button mat-icon-button (click)="incrementLength()" [disabled]="(['policy.write'] | hasRole | async) === false">
|
||||
<mat-icon>add</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<mat-icon class="icon" svgIcon="mdi_numeric"></mat-icon>
|
||||
<span class="left-desc">{{'POLICY.DATA.HASNUMBER' | translate}}</span>
|
||||
<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 || (['policy.write'] | hasRole | async) === false) || (serviceType === PolicyComponentServiceType.ADMIN && (['policy.write'] | hasRole | async) === false)">
|
||||
<mat-slide-toggle
|
||||
color="primary"
|
||||
name="hasNumber"
|
||||
ngDefaultControl
|
||||
[(ngModel)]="complexityData.hasNumber"
|
||||
[disabled]="(['policy.write'] | hasRole | 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="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 || (['policy.write'] | hasRole | async) === false) || (serviceType === PolicyComponentServiceType.ADMIN && (['policy.write'] | hasRole | async) === false)">
|
||||
<mat-slide-toggle
|
||||
color="primary"
|
||||
name="hasSymbol"
|
||||
ngDefaultControl
|
||||
[(ngModel)]="complexityData.hasSymbol"
|
||||
[disabled]="(['policy.write'] | hasRole | async) === false"
|
||||
>
|
||||
</mat-slide-toggle>
|
||||
</div>
|
||||
<div class="row">
|
||||
<mat-icon class="icon" svgIcon="mdi_format-letter-case-lower"></mat-icon>
|
||||
<span class="left-desc">{{'POLICY.DATA.HASLOWERCASE' | translate}}</span>
|
||||
<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 || (['policy.write'] | hasRole | async) === false) || (serviceType === PolicyComponentServiceType.ADMIN && (['policy.write'] | hasRole | async) === false)">
|
||||
<mat-slide-toggle
|
||||
color="primary"
|
||||
name="hasLowercase"
|
||||
ngDefaultControl
|
||||
[(ngModel)]="complexityData.hasLowercase"
|
||||
[disabled]="(['policy.write'] | hasRole | async) === false"
|
||||
>
|
||||
</mat-slide-toggle>
|
||||
</div>
|
||||
<div class="row">
|
||||
<mat-icon class="icon" svgIcon="mdi_format-letter-case-upper"></mat-icon>
|
||||
<span class="left-desc">{{'POLICY.DATA.HASUPPERCASE' | translate}}</span>
|
||||
<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 || (['policy.write'] | hasRole | async) === false) || (serviceType === PolicyComponentServiceType.ADMIN && (['policy.write'] | hasRole | async) === false)">
|
||||
<mat-slide-toggle
|
||||
color="primary"
|
||||
name="hasUppercase"
|
||||
ngDefaultControl
|
||||
[(ngModel)]="complexityData.hasUppercase"
|
||||
[disabled]="(['policy.write'] | hasRole | async) === false"
|
||||
>
|
||||
</mat-slide-toggle>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="btn-container">
|
||||
<button (click)="savePolicy()"
|
||||
[disabled]="serviceType === PolicyComponentServiceType.MGMT && ((['password_complexity_policy'] | hasFeature | async) === false || (['policy.write'] | hasRole | async) === false) || (serviceType === PolicyComponentServiceType.ADMIN && (['policy.write'] | hasRole | async) === false)"
|
||||
color="primary" type="submit" mat-raised-button>{{ 'ACTIONS.SAVE' | translate
|
||||
}}</button>
|
||||
<button
|
||||
(click)="savePolicy()"
|
||||
[disabled]="(['policy.write'] | hasRole | async) === false"
|
||||
color="primary"
|
||||
type="submit"
|
||||
mat-raised-button
|
||||
>
|
||||
{{ 'ACTIONS.SAVE' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<cnsl-policy-grid [currentPolicy]="currentPolicy" [type]="serviceType" tagForFilter="security"></cnsl-policy-grid>
|
||||
</cnsl-detail-layout>
|
||||
</cnsl-detail-layout>
|
||||
|
@@ -7,11 +7,9 @@ import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { HasFeatureModule } from 'src/app/directives/has-feature/has-feature.module';
|
||||
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
|
||||
import { DetailLayoutModule } from 'src/app/modules/detail-layout/detail-layout.module';
|
||||
import { InputModule } from 'src/app/modules/input/input.module';
|
||||
import { HasFeaturePipeModule } from 'src/app/pipes/has-feature-pipe/has-feature-pipe.module';
|
||||
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module';
|
||||
|
||||
import { InfoSectionModule } from '../../info-section/info-section.module';
|
||||
@@ -33,8 +31,6 @@ import { PasswordComplexityPolicyComponent } from './password-complexity-policy.
|
||||
MatTooltipModule,
|
||||
HasRolePipeModule,
|
||||
TranslateModule,
|
||||
HasFeatureModule,
|
||||
HasFeaturePipeModule,
|
||||
DetailLayoutModule,
|
||||
MatProgressSpinnerModule,
|
||||
PolicyGridModule,
|
||||
|
@@ -1,42 +1,40 @@
|
||||
<cnsl-detail-layout [hasBackButton]="true" [title]="'POLICY.PWD_LOCKOUT.TITLE' | translate"
|
||||
[description]="'POLICY.PWD_LOCKOUT.DESCRIPTION' | translate">
|
||||
|
||||
<p class="policy-applied-to" sub>{{'POLICY.APPLIEDTO' | translate}}:
|
||||
<strong *ngIf="orgName; else iam">{{orgName}}</strong>
|
||||
<ng-template #iam><strong>{{'MENU.SYSTEM' | translate}}</strong>
|
||||
<cnsl-detail-layout
|
||||
[hasBackButton]="true"
|
||||
[title]="'POLICY.PWD_LOCKOUT.TITLE' | translate"
|
||||
[description]="'POLICY.PWD_LOCKOUT.DESCRIPTION' | translate"
|
||||
>
|
||||
<p class="policy-applied-to" sub>
|
||||
{{ 'POLICY.APPLIEDTO' | translate }}:
|
||||
<strong *ngIf="orgName; else iam">{{ orgName }}</strong>
|
||||
<ng-template #iam
|
||||
><strong>{{ 'MENU.INSTANCE' | translate }}</strong>
|
||||
</ng-template>
|
||||
</p>
|
||||
<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]="InfoSectionType.WARN">
|
||||
<span [innerHTML]="'FEATURES.NOTAVAILABLE' | translate: ({value: 'lockout_policy'})"></span>
|
||||
</cnsl-info-section>
|
||||
<cnsl-info-section class="default" *ngIf="isDefault"> {{ 'POLICY.DEFAULTLABEL' | translate }}</cnsl-info-section>
|
||||
|
||||
<ng-template cnslHasRole [hasRole]="['policy.delete']">
|
||||
<button
|
||||
[disabled]="serviceType === PolicyComponentServiceType.MGMT && ((['lockout_policy'] | hasFeature | async) === false || (['policy.write'] | hasRole | async) === false) || (serviceType === PolicyComponentServiceType.ADMIN && (['policy.write'] | hasRole | async) === false)"
|
||||
*ngIf="serviceType === PolicyComponentServiceType.MGMT && !isDefault" matTooltip="{{'POLICY.RESET' | translate}}"
|
||||
color="warn" (click)="resetPolicy()" mat-stroked-button>
|
||||
{{'POLICY.RESET' | translate}}
|
||||
[disabled]="(['policy.write'] | hasRole | async) === false"
|
||||
*ngIf="serviceType === PolicyComponentServiceType.MGMT && !isDefault"
|
||||
matTooltip="{{ 'POLICY.RESET' | translate }}"
|
||||
color="warn"
|
||||
(click)="resetPolicy()"
|
||||
mat-stroked-button
|
||||
>
|
||||
{{ 'POLICY.RESET' | translate }}
|
||||
</button>
|
||||
</ng-template>
|
||||
|
||||
<div class="content" *ngIf="lockoutData">
|
||||
<div class="row">
|
||||
<span class="left-desc">{{'POLICY.DATA.MAXATTEMPTS' | translate}}</span>
|
||||
<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 || (['policy.write'] | hasRole | async) === false) || (serviceType === PolicyComponentServiceType.ADMIN && (['policy.write'] | hasRole | async) === false)"
|
||||
mat-icon-button (click)="decrementMaxAttempts()">
|
||||
<button [disabled]="(['policy.write'] | hasRole | 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 || (['policy.write'] | hasRole | async) === false) || (serviceType === PolicyComponentServiceType.ADMIN && (['policy.write'] | hasRole | async) === false)"
|
||||
mat-icon-button (click)="incrementMaxAttempts()">
|
||||
<span>{{ lockoutData?.maxPasswordAttempts }}</span>
|
||||
<button [disabled]="(['policy.write'] | hasRole | async) === false" mat-icon-button (click)="incrementMaxAttempts()">
|
||||
<mat-icon>add</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
@@ -44,12 +42,16 @@
|
||||
</div>
|
||||
|
||||
<div class="btn-container">
|
||||
<button (click)="savePolicy()"
|
||||
[disabled]="serviceType === PolicyComponentServiceType.MGMT && ((['lockout_policy'] | hasFeature | async) === false || (['policy.write'] | hasRole | async) === false) || (serviceType === PolicyComponentServiceType.ADMIN && (['policy.write'] | hasRole | async) === false)"
|
||||
color="primary" type="submit" mat-raised-button>{{ 'ACTIONS.SAVE' | translate
|
||||
}}</button>
|
||||
<button
|
||||
(click)="savePolicy()"
|
||||
[disabled]="(['policy.write'] | hasRole | async) === false"
|
||||
color="primary"
|
||||
type="submit"
|
||||
mat-raised-button
|
||||
>
|
||||
{{ 'ACTIONS.SAVE' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<cnsl-policy-grid [currentPolicy]="currentPolicy" [type]="serviceType" tagForFilter="security"></cnsl-policy-grid>
|
||||
|
||||
</cnsl-detail-layout>
|
||||
</cnsl-detail-layout>
|
||||
|
@@ -9,7 +9,6 @@ import { TranslateModule } from '@ngx-translate/core';
|
||||
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
|
||||
import { DetailLayoutModule } from 'src/app/modules/detail-layout/detail-layout.module';
|
||||
import { InputModule } from 'src/app/modules/input/input.module';
|
||||
import { HasFeaturePipeModule } from 'src/app/pipes/has-feature-pipe/has-feature-pipe.module';
|
||||
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module';
|
||||
|
||||
import { InfoSectionModule } from '../../info-section/info-section.module';
|
||||
@@ -32,7 +31,6 @@ import { PasswordLockoutPolicyComponent } from './password-lockout-policy.compon
|
||||
MatTooltipModule,
|
||||
TranslateModule,
|
||||
DetailLayoutModule,
|
||||
HasFeaturePipeModule,
|
||||
PolicyGridModule,
|
||||
InfoSectionModule,
|
||||
],
|
||||
|
@@ -1,53 +1,64 @@
|
||||
<cnsl-detail-layout [hasBackButton]="true" [title]="'POLICY.PRIVACY_POLICY.TITLE' | translate"
|
||||
[description]="'POLICY.PRIVACY_POLICY.DESCRIPTION' | translate">
|
||||
|
||||
<p class="policy-applied-to" sub>{{'POLICY.APPLIEDTO' | translate}}:
|
||||
<strong *ngIf="orgName; else iam">{{orgName}}</strong>
|
||||
<ng-template #iam><strong>{{'MENU.SYSTEM' | translate}}</strong>
|
||||
<cnsl-detail-layout
|
||||
[hasBackButton]="true"
|
||||
[title]="'POLICY.PRIVACY_POLICY.TITLE' | translate"
|
||||
[description]="'POLICY.PRIVACY_POLICY.DESCRIPTION' | translate"
|
||||
>
|
||||
<p class="policy-applied-to" sub>
|
||||
{{ 'POLICY.APPLIEDTO' | translate }}:
|
||||
<strong *ngIf="orgName; else iam">{{ orgName }}</strong>
|
||||
<ng-template #iam
|
||||
><strong>{{ 'MENU.INSTANCE' | translate }}</strong>
|
||||
</ng-template>
|
||||
</p>
|
||||
|
||||
<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]="InfoSectionType.WARN">
|
||||
<span [innerHTML]="'FEATURES.NOTAVAILABLE' | translate: ({value: 'privacy_policy'})"></span>
|
||||
</cnsl-info-section>
|
||||
<cnsl-info-section *ngIf="isDefault"> {{ 'POLICY.DEFAULTLABEL' | translate }}</cnsl-info-section>
|
||||
|
||||
<div class="divider"></div>
|
||||
|
||||
<div class="content">
|
||||
<form *ngIf="form" [formGroup]="form">
|
||||
<div>
|
||||
<form *ngIf="form" [formGroup]="form" class="content">
|
||||
<cnsl-form-field class="privacy-policy-formfield">
|
||||
<cnsl-label>{{ 'POLICY.PRIVACY_POLICY.TOSLINK' | translate }}</cnsl-label>
|
||||
<input cnslInput name="tosLink" formControlName="tosLink" />
|
||||
<template [ngTemplateOutlet]="templateRef" [ngTemplateOutletContext]="{key: 'tosLink'}"></template>
|
||||
<template [ngTemplateOutlet]="templateRef" [ngTemplateOutletContext]="{ key: 'tosLink' }"></template>
|
||||
</cnsl-form-field>
|
||||
|
||||
<cnsl-form-field class="privacy-policy-formfield">
|
||||
<cnsl-label>{{ 'POLICY.PRIVACY_POLICY.POLICYLINK' | translate }}</cnsl-label>
|
||||
<input cnslInput name="privacyLink" formControlName="privacyLink" />
|
||||
<template [ngTemplateOutlet]="templateRef" [ngTemplateOutletContext]="{key: 'privacyLink'}"></template>
|
||||
<template [ngTemplateOutlet]="templateRef" [ngTemplateOutletContext]="{ key: 'privacyLink' }"></template>
|
||||
</cnsl-form-field>
|
||||
|
||||
<cnsl-form-field class="privacy-policy-formfield">
|
||||
<cnsl-label>{{ 'POLICY.PRIVACY_POLICY.HELPLINK' | translate }}</cnsl-label>
|
||||
<input cnslInput name="helpLink" formControlName="helpLink" />
|
||||
<template [ngTemplateOutlet]="templateRef" [ngTemplateOutletContext]="{key: 'helpLink'}"></template>
|
||||
<template [ngTemplateOutlet]="templateRef" [ngTemplateOutletContext]="{ key: 'helpLink' }"></template>
|
||||
</cnsl-form-field>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
<button *ngIf="privacyPolicy && privacyPolicy.isDefault === false" class="reset-button"
|
||||
[disabled]="(serviceType === PolicyComponentServiceType.MGMT && ((['privacy_policy'] | hasFeature | async) === false) || (canWrite$ | 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) || (canWrite$ | async) === false)"
|
||||
(click)="saveCurrentMessage()" color="primary" type="submit" mat-raised-button>{{ 'ACTIONS.SAVE' | translate
|
||||
}}</button>
|
||||
<button
|
||||
*ngIf="privacyPolicy && privacyPolicy.isDefault === false"
|
||||
class="reset-button"
|
||||
[disabled]="(canWrite$ | 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]="(canWrite$ | async) === false"
|
||||
(click)="saveCurrentMessage()"
|
||||
color="primary"
|
||||
type="submit"
|
||||
mat-raised-button
|
||||
>
|
||||
{{ 'ACTIONS.SAVE' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<cnsl-policy-grid [currentPolicy]="currentPolicy" [type]="serviceType" tagForFilter="text"></cnsl-policy-grid>
|
||||
@@ -55,9 +66,14 @@
|
||||
|
||||
<ng-template #templateRef let-key="key">
|
||||
<div class="chips">
|
||||
<div class="chip" cnslCopyToClipboard [valueToCopy]="LANGPLACEHOLDER" (copiedValue)="copied = $event"
|
||||
(click)="addChip(key, LANGPLACEHOLDER)">
|
||||
<span class="key">{{LANGPLACEHOLDER}}</span>
|
||||
<div
|
||||
class="chip"
|
||||
cnslCopyToClipboard
|
||||
[valueToCopy]="LANGPLACEHOLDER"
|
||||
(copiedValue)="copied = $event"
|
||||
(click)="addChip(key, LANGPLACEHOLDER)"
|
||||
>
|
||||
<span class="key">{{ LANGPLACEHOLDER }}</span>
|
||||
<i *ngIf="copied !== LANGPLACEHOLDER" class="las la-clipboard"></i>
|
||||
<i *ngIf="copied === LANGPLACEHOLDER" class="las la-clipboard-check"></i>
|
||||
</div>
|
||||
|
@@ -26,6 +26,13 @@
|
||||
|
||||
.content {
|
||||
padding-top: 1rem;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
column-gap: 1rem;
|
||||
|
||||
@media only screen and (min-width: 800px) {
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.privacy-policy-formfield {
|
||||
|
@@ -14,7 +14,6 @@ import { CopyToClipboardModule } from 'src/app/directives/copy-to-clipboard/copy
|
||||
import { HasRoleModule } from '../../../directives/has-role/has-role.module';
|
||||
import { DetailLayoutModule } from '../../../modules/detail-layout/detail-layout.module';
|
||||
import { InputModule } from '../../../modules/input/input.module';
|
||||
import { HasFeaturePipeModule } from '../../../pipes/has-feature-pipe/has-feature-pipe.module';
|
||||
import { HasRolePipeModule } from '../../../pipes/has-role-pipe/has-role-pipe.module';
|
||||
import { FormFieldModule } from '../../form-field/form-field.module';
|
||||
import { InfoSectionModule } from '../../info-section/info-section.module';
|
||||
@@ -35,7 +34,6 @@ import { PrivacyPolicyComponent } from './privacy-policy.component';
|
||||
FormFieldModule,
|
||||
CopyToClipboardModule,
|
||||
MatButtonModule,
|
||||
HasFeaturePipeModule,
|
||||
MatIconModule,
|
||||
HasRoleModule,
|
||||
HasRolePipeModule,
|
||||
|
@@ -1,18 +1,14 @@
|
||||
<cnsl-detail-layout [hasBackButton]="true" [title]="'POLICY.PRIVATELABELING.TITLE' | translate">
|
||||
<p class="policy-applied-to" sub>{{'POLICY.APPLIEDTO' | translate}}:
|
||||
<strong *ngIf="org; else iam">{{org.name}}</strong>
|
||||
<ng-template #iam><strong>{{'MENU.SYSTEM' | translate}}</strong>
|
||||
<p class="policy-applied-to" sub>
|
||||
{{ 'POLICY.APPLIEDTO' | translate }}:
|
||||
<strong *ngIf="org; else iam">{{ org.name }}</strong>
|
||||
<ng-template #iam
|
||||
><strong>{{ 'MENU.INSTANCE' | translate }}</strong>
|
||||
</ng-template>
|
||||
</p>
|
||||
|
||||
<div class="privatelabeling-policy">
|
||||
<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]="InfoSectionType.WARN">
|
||||
<span [innerHTML]="'FEATURES.NOTAVAILABLE' | translate: ({value: 'label_policy.private_label'})"></span>
|
||||
</cnsl-info-section>
|
||||
<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>
|
||||
@@ -23,30 +19,35 @@
|
||||
<mat-button-toggle [value]="Theme.LIGHT">
|
||||
<div class="toggle-row">
|
||||
<i class="icon las la-sun"></i>
|
||||
<span>{{'POLICY.PRIVATELABELING.LIGHT' | translate}}</span>
|
||||
<span>{{ 'POLICY.PRIVATELABELING.LIGHT' | translate }}</span>
|
||||
<div *ngIf="theme === Theme.LIGHT" class="current-dot"></div>
|
||||
</div>
|
||||
</mat-button-toggle>
|
||||
<mat-button-toggle [value]=" Theme.DARK">
|
||||
<mat-button-toggle [value]="Theme.DARK">
|
||||
<div class="toggle-row">
|
||||
<i class="icon las la-moon"></i>
|
||||
<span> {{'POLICY.PRIVATELABELING.DARK' | translate}}</span>
|
||||
<span> {{ 'POLICY.PRIVATELABELING.DARK' | translate }}</span>
|
||||
<div *ngIf="theme === Theme.DARK" class="current-dot"></div>
|
||||
</div>
|
||||
</mat-button-toggle>
|
||||
</mat-button-toggle-group>
|
||||
|
||||
<mat-button-toggle-group class="theme-toggle" class="buttongroup" [(ngModel)]="view" name="displayview"
|
||||
aria-label="Display View">
|
||||
<mat-button-toggle-group
|
||||
class="theme-toggle"
|
||||
class="buttongroup"
|
||||
[(ngModel)]="view"
|
||||
name="displayview"
|
||||
aria-label="Display View"
|
||||
>
|
||||
<mat-button-toggle [value]="View.PREVIEW">
|
||||
<div class="toggle-row">
|
||||
<span>{{'POLICY.PRIVATELABELING.VIEWS.PREVIEW' | translate}}</span>
|
||||
<span>{{ 'POLICY.PRIVATELABELING.VIEWS.PREVIEW' | translate }}</span>
|
||||
<div *ngIf="view === View.PREVIEW" class="current-dot"></div>
|
||||
</div>
|
||||
</mat-button-toggle>
|
||||
<mat-button-toggle [value]="View.CURRENT">
|
||||
<div class="toggle-row">
|
||||
<span> {{'POLICY.PRIVATELABELING.VIEWS.CURRENT' | translate}}</span>
|
||||
<span> {{ 'POLICY.PRIVATELABELING.VIEWS.CURRENT' | translate }}</span>
|
||||
<div *ngIf="view === View.CURRENT" class="current-dot"></div>
|
||||
</div>
|
||||
</mat-button-toggle>
|
||||
@@ -55,22 +56,31 @@
|
||||
<span class="fill-space"></span>
|
||||
|
||||
<ng-template cnslHasRole [hasRole]="['policy.delete']">
|
||||
<button class="pl-action-button"
|
||||
<button
|
||||
class="pl-action-button"
|
||||
*ngIf="view === View.CURRENT && serviceType === PolicyComponentServiceType.MGMT && !isDefault"
|
||||
matTooltip="{{'POLICY.RESET' | translate}}" color="warn" (click)="removePolicy()" mat-stroked-button>
|
||||
{{'POLICY.RESET' | translate}}
|
||||
matTooltip="{{ 'POLICY.RESET' | translate }}"
|
||||
color="warn"
|
||||
(click)="removePolicy()"
|
||||
mat-stroked-button
|
||||
>
|
||||
{{ 'POLICY.RESET' | translate }}
|
||||
</button>
|
||||
</ng-template>
|
||||
|
||||
<button *ngIf="view === View.PREVIEW" class="pl-action-button"
|
||||
[disabled]="(serviceType === PolicyComponentServiceType.MGMT && (['label_policy.private_label'] | hasFeature | async) === false)"
|
||||
mat-raised-button color="primary" (click)="activatePolicy()">
|
||||
{{'POLICY.PRIVATELABELING.ACTIVATEPREVIEW' | translate}}
|
||||
<button
|
||||
*ngIf="view === View.PREVIEW"
|
||||
class="pl-action-button"
|
||||
mat-raised-button
|
||||
color="primary"
|
||||
(click)="activatePolicy()"
|
||||
>
|
||||
{{ 'POLICY.PRIVATELABELING.ACTIVATEPREVIEW' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<cnsl-info-section *ngIf="view === View.PREVIEW" class="desc cnsl-secondary-text">
|
||||
{{'POLICY.PRIVATELABELING.PREVIEW_DESCRIPTION' | translate}}
|
||||
{{ 'POLICY.PRIVATELABELING.PREVIEW_DESCRIPTION' | translate }}
|
||||
</cnsl-info-section>
|
||||
|
||||
<div *ngIf="previewData && data" class="lab-policy-content">
|
||||
@@ -82,42 +92,67 @@
|
||||
<i class="icon las la-image"></i>
|
||||
<span>Logos</span>
|
||||
<span class="space"></span>
|
||||
<small class="cnsl-secondary-text" *ngIf="theme === Theme.DARK">({{'POLICY.PRIVATELABELING.DARK' |
|
||||
translate}})</small>
|
||||
<small class="cnsl-secondary-text" *ngIf="theme === Theme.LIGHT">({{'POLICY.PRIVATELABELING.LIGHT' |
|
||||
translate}})</small>
|
||||
<small class="cnsl-secondary-text" *ngIf="theme === Theme.DARK"
|
||||
>({{ 'POLICY.PRIVATELABELING.DARK' | translate }})</small
|
||||
>
|
||||
<small class="cnsl-secondary-text" *ngIf="theme === Theme.LIGHT"
|
||||
>({{ 'POLICY.PRIVATELABELING.LIGHT' | translate }})</small
|
||||
>
|
||||
</div>
|
||||
</mat-panel-title>
|
||||
</mat-expansion-panel-header>
|
||||
|
||||
<div>
|
||||
<p class="cnsl-secondary-button">{{'POLICY.PRIVATELABELING.USEOFLOGO' | translate}}</p>
|
||||
<p class="cnsl-secondary-button">{{ 'POLICY.PRIVATELABELING.USEOFLOGO' | translate }}</p>
|
||||
|
||||
<cnsl-info-section *ngIf="view !== View.CURRENT" class="max-size-desc"> {{'POLICY.PRIVATELABELING.MAXSIZE' |
|
||||
translate}}
|
||||
<cnsl-info-section *ngIf="view !== View.CURRENT" class="max-size-desc">
|
||||
{{ 'POLICY.PRIVATELABELING.MAXSIZE' | translate }}
|
||||
</cnsl-info-section>
|
||||
<cnsl-info-section *ngIf="view !== View.CURRENT" class="max-size-desc">
|
||||
{{'POLICY.PRIVATELABELING.EMAILNOSVG' | translate}}
|
||||
{{ 'POLICY.PRIVATELABELING.EMAILNOSVG' | translate }}
|
||||
</cnsl-info-section>
|
||||
|
||||
<div class="logo-view" [attr.data-e2e]="'image-part-logo'">
|
||||
<span class="label cnsl-secondary-text">Logo</span>
|
||||
<div class="img-wrapper">
|
||||
<ng-container
|
||||
*ngIf="view === View.PREVIEW ? (theme === Theme.DARK ? previewData.logoUrlDark : previewData.logoUrl): (theme === Theme.DARK ? data.logoUrlDark : data.logoUrl) as logoSrc; else addLogoButton">
|
||||
*ngIf="
|
||||
view === View.PREVIEW
|
||||
? theme === Theme.DARK
|
||||
? previewData.logoUrlDark
|
||||
: previewData.logoUrl
|
||||
: theme === Theme.DARK
|
||||
? data.logoUrlDark
|
||||
: data.logoUrl as logoSrc;
|
||||
else addLogoButton
|
||||
"
|
||||
>
|
||||
<img [src]="logoSrc" alt="logo" />
|
||||
<button class="dl-btn" mat-icon-button color="warn" (click)="deleteAsset(AssetType.LOGO, theme)"
|
||||
[disabled]="view === View.CURRENT || (serviceType === PolicyComponentServiceType.MGMT && (['label_policy.private_label'] | hasFeature | async) === false)"
|
||||
matTooltip="{{'ACTIONS.DELETE' | translate}}">
|
||||
<button
|
||||
class="dl-btn"
|
||||
mat-icon-button
|
||||
color="warn"
|
||||
(click)="deleteAsset(AssetType.LOGO, theme)"
|
||||
[disabled]="view === View.CURRENT"
|
||||
matTooltip="{{ 'ACTIONS.DELETE' | translate }}"
|
||||
>
|
||||
<i class="las la-ban"></i>
|
||||
</button>
|
||||
</ng-container>
|
||||
<ng-template #addLogoButton>
|
||||
<input #selectedFile style="display: none;" class="file-input" type="file"
|
||||
(change)="onDropLogo(theme, $any($event.target).files)">
|
||||
<button mat-icon-button matTooltip="{{'POLICY.PRIVATELABELING.BTN' | translate}}"
|
||||
*ngIf="view !== View.CURRENT && (serviceType === PolicyComponentServiceType.ADMIN || (serviceType === PolicyComponentServiceType.MGMT && (['label_policy.private_label'] | hasFeature | async)))"
|
||||
(click)="$event.preventDefault(); selectedFile.click();">
|
||||
<input
|
||||
#selectedFile
|
||||
style="display: none"
|
||||
class="file-input"
|
||||
type="file"
|
||||
(change)="onDropLogo(theme, $any($event.target).files)"
|
||||
/>
|
||||
<button
|
||||
mat-icon-button
|
||||
matTooltip="{{ 'POLICY.PRIVATELABELING.BTN' | translate }}"
|
||||
*ngIf="view !== View.CURRENT"
|
||||
(click)="$event.preventDefault(); selectedFile.click()"
|
||||
>
|
||||
<mat-icon>add</mat-icon>
|
||||
</button>
|
||||
</ng-template>
|
||||
@@ -128,20 +163,43 @@
|
||||
<span class="label cnsl-secondary-text">Icon</span>
|
||||
<div class="img-wrapper icon">
|
||||
<ng-container
|
||||
*ngIf="view === View.PREVIEW ? (theme === Theme.DARK ? previewData.iconUrlDark : previewData.iconUrl): (theme === Theme.DARK ? data.iconUrlDark : data.iconUrl) as iconSrc; else addIconButton">
|
||||
*ngIf="
|
||||
view === View.PREVIEW
|
||||
? theme === Theme.DARK
|
||||
? previewData.iconUrlDark
|
||||
: previewData.iconUrl
|
||||
: theme === Theme.DARK
|
||||
? data.iconUrlDark
|
||||
: data.iconUrl as iconSrc;
|
||||
else addIconButton
|
||||
"
|
||||
>
|
||||
<img [src]="iconSrc" alt="icon" />
|
||||
<button class="dl-btn" mat-icon-button color="warn" (click)="deleteAsset(AssetType.ICON, theme)"
|
||||
[disabled]="view === View.CURRENT || (serviceType === PolicyComponentServiceType.MGMT && (['label_policy.private_label'] | hasFeature | async) === false)"
|
||||
matTooltip="{{'ACTIONS.DELETE' | translate}}">
|
||||
<button
|
||||
class="dl-btn"
|
||||
mat-icon-button
|
||||
color="warn"
|
||||
(click)="deleteAsset(AssetType.ICON, theme)"
|
||||
[disabled]="view === View.CURRENT"
|
||||
matTooltip="{{ 'ACTIONS.DELETE' | translate }}"
|
||||
>
|
||||
<i class="las la-ban"></i>
|
||||
</button>
|
||||
</ng-container>
|
||||
<ng-template #addIconButton>
|
||||
<input #selectedIconFile style="display: none;" class="file-input" type="file"
|
||||
(change)="onDropIcon(theme, $any($event.target).files)">
|
||||
<button mat-icon-button matTooltip="{{'POLICY.PRIVATELABELING.BTN' | translate}}"
|
||||
*ngIf="view !== View.CURRENT && (serviceType === PolicyComponentServiceType.ADMIN || (serviceType === PolicyComponentServiceType.MGMT && (['label_policy.private_label'] | hasFeature | async)))"
|
||||
(click)="$event.preventDefault(); selectedIconFile.click();">
|
||||
<input
|
||||
#selectedIconFile
|
||||
style="display: none"
|
||||
class="file-input"
|
||||
type="file"
|
||||
(change)="onDropIcon(theme, $any($event.target).files)"
|
||||
/>
|
||||
<button
|
||||
mat-icon-button
|
||||
matTooltip="{{ 'POLICY.PRIVATELABELING.BTN' | translate }}"
|
||||
*ngIf="view !== View.CURRENT"
|
||||
(click)="$event.preventDefault(); selectedIconFile.click()"
|
||||
>
|
||||
<mat-icon>add</mat-icon>
|
||||
</button>
|
||||
</ng-template>
|
||||
@@ -155,12 +213,14 @@
|
||||
<mat-panel-title>
|
||||
<div class="panel-title">
|
||||
<i class="icon las la-palette"></i>
|
||||
<span>{{'POLICY.PRIVATELABELING.COLORS' | translate}}</span>
|
||||
<span>{{ 'POLICY.PRIVATELABELING.COLORS' | translate }}</span>
|
||||
<span class="space"></span>
|
||||
<small class="cnsl-secondary-text" *ngIf="theme === Theme.DARK">({{'POLICY.PRIVATELABELING.DARK' |
|
||||
translate}})</small>
|
||||
<small class="cnsl-secondary-text" *ngIf="theme === Theme.LIGHT">({{'POLICY.PRIVATELABELING.LIGHT' |
|
||||
translate}})</small>
|
||||
<small class="cnsl-secondary-text" *ngIf="theme === Theme.DARK"
|
||||
>({{ 'POLICY.PRIVATELABELING.DARK' | translate }})</small
|
||||
>
|
||||
<small class="cnsl-secondary-text" *ngIf="theme === Theme.LIGHT"
|
||||
>({{ 'POLICY.PRIVATELABELING.LIGHT' | translate }})</small
|
||||
>
|
||||
</div>
|
||||
</mat-panel-title>
|
||||
</mat-expansion-panel-header>
|
||||
@@ -168,29 +228,49 @@
|
||||
<ng-container *ngIf="theme === Theme.DARK">
|
||||
<div class="colors" *ngIf="data && previewData">
|
||||
<div class="color">
|
||||
<cnsl-color [disabled]="view === View.CURRENT" [colorType]="ColorType.BACKGROUNDDARK"
|
||||
(previewChanged)="previewData.backgroundColorDark = $event; savePolicy()" name="Background Color"
|
||||
[color]="data.backgroundColorDark" [previewColor]="previewData.backgroundColorDark"></cnsl-color>
|
||||
<cnsl-color
|
||||
[disabled]="view === View.CURRENT"
|
||||
[colorType]="ColorType.BACKGROUNDDARK"
|
||||
(previewChanged)="previewData.backgroundColorDark = $event; savePolicy()"
|
||||
name="Background Color"
|
||||
[color]="data.backgroundColorDark"
|
||||
[previewColor]="previewData.backgroundColorDark"
|
||||
></cnsl-color>
|
||||
</div>
|
||||
|
||||
<div class="color">
|
||||
<cnsl-color [disabled]="view === View.CURRENT" [colorType]="ColorType.PRIMARY"
|
||||
(previewChanged)="previewData.primaryColorDark = $event; savePolicy()" name="Primary Color"
|
||||
[color]="data.primaryColorDark" [previewColor]="previewData.primaryColorDark">
|
||||
<cnsl-color
|
||||
[disabled]="view === View.CURRENT"
|
||||
[colorType]="ColorType.PRIMARY"
|
||||
(previewChanged)="previewData.primaryColorDark = $event; savePolicy()"
|
||||
name="Primary Color"
|
||||
[color]="data.primaryColorDark"
|
||||
[previewColor]="previewData.primaryColorDark"
|
||||
>
|
||||
</cnsl-color>
|
||||
</div>
|
||||
|
||||
<div class="color">
|
||||
<cnsl-color [disabled]="view === View.CURRENT" [colorType]="ColorType.WARN"
|
||||
(previewChanged)="previewData.warnColorDark = $event; savePolicy()" name="Warn Color"
|
||||
[color]="data.warnColorDark" [previewColor]="previewData.warnColorDark">
|
||||
<cnsl-color
|
||||
[disabled]="view === View.CURRENT"
|
||||
[colorType]="ColorType.WARN"
|
||||
(previewChanged)="previewData.warnColorDark = $event; savePolicy()"
|
||||
name="Warn Color"
|
||||
[color]="data.warnColorDark"
|
||||
[previewColor]="previewData.warnColorDark"
|
||||
>
|
||||
</cnsl-color>
|
||||
</div>
|
||||
|
||||
<div class="color">
|
||||
<cnsl-color [disabled]="view === View.CURRENT" [colorType]="ColorType.FONTDARK"
|
||||
(previewChanged)="previewData.fontColorDark = $event; savePolicy()" name="Font Color"
|
||||
[color]="data.fontColorDark" [previewColor]="previewData.fontColorDark">
|
||||
<cnsl-color
|
||||
[disabled]="view === View.CURRENT"
|
||||
[colorType]="ColorType.FONTDARK"
|
||||
(previewChanged)="previewData.fontColorDark = $event; savePolicy()"
|
||||
name="Font Color"
|
||||
[color]="data.fontColorDark"
|
||||
[previewColor]="previewData.fontColorDark"
|
||||
>
|
||||
</cnsl-color>
|
||||
</div>
|
||||
</div>
|
||||
@@ -199,28 +279,48 @@
|
||||
<ng-container *ngIf="theme === Theme.LIGHT">
|
||||
<div class="colors" *ngIf="data && previewData">
|
||||
<div class="color">
|
||||
<cnsl-color [disabled]="view === View.CURRENT" [colorType]="ColorType.BACKGROUNDLIGHT"
|
||||
(previewChanged)="previewData.backgroundColor = $event; savePolicy()" name="Background Color"
|
||||
[color]="data.backgroundColor" [previewColor]="previewData.backgroundColor"></cnsl-color>
|
||||
<cnsl-color
|
||||
[disabled]="view === View.CURRENT"
|
||||
[colorType]="ColorType.BACKGROUNDLIGHT"
|
||||
(previewChanged)="previewData.backgroundColor = $event; savePolicy()"
|
||||
name="Background Color"
|
||||
[color]="data.backgroundColor"
|
||||
[previewColor]="previewData.backgroundColor"
|
||||
></cnsl-color>
|
||||
</div>
|
||||
|
||||
<div class="color">
|
||||
<cnsl-color [disabled]="view === View.CURRENT" [colorType]="ColorType.PRIMARY"
|
||||
(previewChanged)="previewData.primaryColor = $event; savePolicy()" name="Primary Color"
|
||||
[color]="data.primaryColor" [previewColor]="previewData.primaryColor">
|
||||
<cnsl-color
|
||||
[disabled]="view === View.CURRENT"
|
||||
[colorType]="ColorType.PRIMARY"
|
||||
(previewChanged)="previewData.primaryColor = $event; savePolicy()"
|
||||
name="Primary Color"
|
||||
[color]="data.primaryColor"
|
||||
[previewColor]="previewData.primaryColor"
|
||||
>
|
||||
</cnsl-color>
|
||||
</div>
|
||||
|
||||
<div class="color">
|
||||
<cnsl-color [disabled]="view === View.CURRENT" [colorType]="ColorType.WARN" name="Warn Color"
|
||||
(previewChanged)="previewData.warnColor= $event; savePolicy()" [color]="data.warnColor"
|
||||
[previewColor]="previewData.warnColor"></cnsl-color>
|
||||
<cnsl-color
|
||||
[disabled]="view === View.CURRENT"
|
||||
[colorType]="ColorType.WARN"
|
||||
name="Warn Color"
|
||||
(previewChanged)="previewData.warnColor = $event; savePolicy()"
|
||||
[color]="data.warnColor"
|
||||
[previewColor]="previewData.warnColor"
|
||||
></cnsl-color>
|
||||
</div>
|
||||
|
||||
<div class="color">
|
||||
<cnsl-color [disabled]="view === View.CURRENT" [colorType]="ColorType.FONTLIGHT"
|
||||
(previewChanged)="previewData.fontColor = $event; savePolicy()" name="Font Color"
|
||||
[color]="data.fontColor" [previewColor]="previewData.fontColor"></cnsl-color>
|
||||
<cnsl-color
|
||||
[disabled]="view === View.CURRENT"
|
||||
[colorType]="ColorType.FONTLIGHT"
|
||||
(previewChanged)="previewData.fontColor = $event; savePolicy()"
|
||||
name="Font Color"
|
||||
[color]="data.fontColor"
|
||||
[previewColor]="previewData.fontColor"
|
||||
></cnsl-color>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
@@ -231,33 +331,53 @@
|
||||
<mat-panel-title>
|
||||
<div class="panel-title">
|
||||
<i class="icon las la-font"></i>
|
||||
{{'POLICY.PRIVATELABELING.FONT' | translate}}
|
||||
{{ 'POLICY.PRIVATELABELING.FONT' | translate }}
|
||||
</div>
|
||||
</mat-panel-title>
|
||||
</mat-expansion-panel-header>
|
||||
<div class="fonts">
|
||||
<cnsl-info-section class="info-section">{{'POLICY.PRIVATELABELING.FONTINLOGINONLY' | translate}}
|
||||
<cnsl-info-section class="info-section"
|
||||
>{{ 'POLICY.PRIVATELABELING.FONTINLOGINONLY' | translate }}
|
||||
</cnsl-info-section>
|
||||
<div class="font-preview" *ngIf="previewData.fontUrl; else addFontButton">
|
||||
<mat-icon class="icon">text_fields</mat-icon>
|
||||
|
||||
<span class="fill-space"></span>
|
||||
|
||||
<button class="dl-btn"
|
||||
[disabled]="view === View.CURRENT || (serviceType === PolicyComponentServiceType.MGMT && (['label_policy.private_label'] | hasFeature | async) === false)"
|
||||
mat-icon-button color="warn" (click)="deleteFont()" matTooltip="{{'ACTIONS.DELETE' | translate}}">
|
||||
<button
|
||||
class="dl-btn"
|
||||
[disabled]="view === View.CURRENT"
|
||||
mat-icon-button
|
||||
color="warn"
|
||||
(click)="deleteFont()"
|
||||
matTooltip="{{ 'ACTIONS.DELETE' | translate }}"
|
||||
>
|
||||
<i class="las la-ban"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<ng-template #addFontButton>
|
||||
<div class="font-add" cnslDropzone (hovered)="toggleHoverFont($event)" (dropped)="onDropFont($event)"
|
||||
[class.hovering]="isHoveringOverFont">
|
||||
<input #selectedFontFile style="display: none;" class="file-input" type="file"
|
||||
(change)="onDropFont($any($event.target).files)">
|
||||
<a class="btn" mat-icon-button
|
||||
*ngIf="view !== View.CURRENT && (serviceType === PolicyComponentServiceType.ADMIN || (serviceType === PolicyComponentServiceType.MGMT && (['label_policy.private_label'] | hasFeature | async)))"
|
||||
(click)="selectedFontFile.click()" matTooltip="{{'POLICY.PRIVATELABELING.BTN' | translate}}">
|
||||
<div
|
||||
class="font-add"
|
||||
cnslDropzone
|
||||
(hovered)="toggleHoverFont($event)"
|
||||
(dropped)="onDropFont($event)"
|
||||
[class.hovering]="isHoveringOverFont"
|
||||
>
|
||||
<input
|
||||
#selectedFontFile
|
||||
style="display: none"
|
||||
class="file-input"
|
||||
type="file"
|
||||
(change)="onDropFont($any($event.target).files)"
|
||||
/>
|
||||
<a
|
||||
class="btn"
|
||||
mat-icon-button
|
||||
*ngIf="view !== View.CURRENT"
|
||||
(click)="selectedFontFile.click()"
|
||||
matTooltip="{{ 'POLICY.PRIVATELABELING.BTN' | translate }}"
|
||||
>
|
||||
<mat-icon>add</mat-icon>
|
||||
</a>
|
||||
</div>
|
||||
@@ -270,37 +390,31 @@
|
||||
<mat-panel-title>
|
||||
<div class="panel-title">
|
||||
<i class="icon las la-universal-access"></i>
|
||||
{{'POLICY.PRIVATELABELING.ADVANCEDBEHAVIOR' | translate}}
|
||||
{{ 'POLICY.PRIVATELABELING.ADVANCEDBEHAVIOR' | translate }}
|
||||
</div>
|
||||
</mat-panel-title>
|
||||
</mat-expansion-panel-header>
|
||||
<div class="adv-container" *ngIf="previewData">
|
||||
|
||||
<ng-container
|
||||
*ngIf="view !== View.CURRENT && 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]="view === View.CURRENT || (serviceType === PolicyComponentServiceType.MGMT && (['label_policy.private_label'] | hasFeature | async) === false)"
|
||||
<mat-slide-toggle
|
||||
class="toggle"
|
||||
color="primary"
|
||||
ngDefaultControl
|
||||
[disabled]="view === View.CURRENT"
|
||||
[(ngModel)]="view === View.CURRENT ? data.hideLoginNameSuffix : previewData.hideLoginNameSuffix"
|
||||
(change)="savePolicy()">
|
||||
{{'POLICY.DATA.HIDELOGINNAMESUFFIX' | translate}}
|
||||
(change)="savePolicy()"
|
||||
>
|
||||
{{ 'POLICY.DATA.HIDELOGINNAMESUFFIX' | translate }}
|
||||
</mat-slide-toggle>
|
||||
|
||||
<ng-container
|
||||
*ngIf="view !== View.CURRENT && 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]="view === View.CURRENT || (serviceType === PolicyComponentServiceType.MGMT && (['label_policy.watermark'] | hasFeature | async) === false)"
|
||||
<mat-slide-toggle
|
||||
class="toggle"
|
||||
color="primary"
|
||||
ngDefaultControl
|
||||
[disabled]="view === View.CURRENT"
|
||||
[(ngModel)]="view === View.CURRENT ? data.disableWatermark : previewData.disableWatermark"
|
||||
(change)="savePolicy()">
|
||||
{{'POLICY.DATA.DISABLEWATERMARK' | translate}}
|
||||
(change)="savePolicy()"
|
||||
>
|
||||
{{ 'POLICY.DATA.DISABLEWATERMARK' | translate }}
|
||||
</mat-slide-toggle>
|
||||
</div>
|
||||
</mat-expansion-panel>
|
||||
@@ -310,8 +424,12 @@
|
||||
|
||||
<div class="preview-wrapper">
|
||||
<div class="col">
|
||||
<cnsl-preview [refresh]="refreshPreview" [theme]="theme" class="preview"
|
||||
[policy]="view === View.PREVIEW ? previewData : data">
|
||||
<cnsl-preview
|
||||
[refresh]="refreshPreview"
|
||||
[theme]="theme"
|
||||
class="preview"
|
||||
[policy]="view === View.PREVIEW ? previewData : data"
|
||||
>
|
||||
</cnsl-preview>
|
||||
</div>
|
||||
</div>
|
||||
@@ -320,4 +438,4 @@
|
||||
<cnsl-policy-grid class="grid" [currentPolicy]="currentPolicy" [type]="serviceType" tagForFilter="text">
|
||||
</cnsl-policy-grid>
|
||||
</div>
|
||||
</cnsl-detail-layout>
|
||||
</cnsl-detail-layout>
|
||||
|
@@ -119,6 +119,7 @@
|
||||
.expansion {
|
||||
box-shadow: none;
|
||||
border: 1px solid $p-border-color;
|
||||
background-color: map-get($background, cards);
|
||||
|
||||
.header {
|
||||
justify-content: flex-start;
|
||||
|
@@ -2,17 +2,17 @@ import { HttpErrorResponse } from '@angular/common/http';
|
||||
import { Component, EventEmitter, Injector, OnDestroy, OnInit, Type } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { Subject, Subscription } from 'rxjs';
|
||||
import { switchMap, take, takeUntil } from 'rxjs/operators';
|
||||
import { switchMap, takeUntil } from 'rxjs/operators';
|
||||
import {
|
||||
GetLabelPolicyResponse as AdminGetLabelPolicyResponse,
|
||||
GetPreviewLabelPolicyResponse as AdminGetPreviewLabelPolicyResponse,
|
||||
UpdateLabelPolicyRequest,
|
||||
GetLabelPolicyResponse as AdminGetLabelPolicyResponse,
|
||||
GetPreviewLabelPolicyResponse as AdminGetPreviewLabelPolicyResponse,
|
||||
UpdateLabelPolicyRequest,
|
||||
} from 'src/app/proto/generated/zitadel/admin_pb';
|
||||
import {
|
||||
AddCustomLabelPolicyRequest,
|
||||
GetLabelPolicyResponse as MgmtGetLabelPolicyResponse,
|
||||
GetPreviewLabelPolicyResponse as MgmtGetPreviewLabelPolicyResponse,
|
||||
UpdateCustomLabelPolicyRequest,
|
||||
AddCustomLabelPolicyRequest,
|
||||
GetLabelPolicyResponse as MgmtGetLabelPolicyResponse,
|
||||
GetPreviewLabelPolicyResponse as MgmtGetPreviewLabelPolicyResponse,
|
||||
UpdateCustomLabelPolicyRequest,
|
||||
} from 'src/app/proto/generated/zitadel/management_pb';
|
||||
import { Org } from 'src/app/proto/generated/zitadel/org_pb';
|
||||
import { LabelPolicy } from 'src/app/proto/generated/zitadel/policy_pb';
|
||||
@@ -371,31 +371,26 @@ export class PrivateLabelingPolicyComponent implements OnInit, OnDestroy {
|
||||
public fetchData(): void {
|
||||
this.loading = true;
|
||||
|
||||
this.authService
|
||||
.canUseFeature(['label_policy.private_label'])
|
||||
.pipe(take(1))
|
||||
.subscribe((canUse) => {
|
||||
this.getPreviewData()
|
||||
.then((data) => {
|
||||
if (data.policy) {
|
||||
this.previewData = data.policy;
|
||||
this.loading = false;
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
this.getPreviewData()
|
||||
.then((data) => {
|
||||
if (data.policy) {
|
||||
this.previewData = data.policy;
|
||||
this.loading = false;
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
|
||||
this.getData()
|
||||
.then((data) => {
|
||||
if (data.policy) {
|
||||
this.data = data.policy;
|
||||
this.loading = false;
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
this.getData()
|
||||
.then((data) => {
|
||||
if (data.policy) {
|
||||
this.data = data.policy;
|
||||
this.loading = false;
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toast.showError(error);
|
||||
});
|
||||
}
|
||||
|
||||
|
@@ -12,7 +12,6 @@ import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { ColorChromeModule } from 'ngx-color/chrome';
|
||||
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
|
||||
import { HasFeaturePipeModule } from 'src/app/pipes/has-feature-pipe/has-feature-pipe.module';
|
||||
|
||||
import { DropzoneModule } from '../../../directives/dropzone/dropzone.module';
|
||||
import { DetailLayoutModule } from '../../detail-layout/detail-layout.module';
|
||||
@@ -46,7 +45,6 @@ import { PrivateLabelingPolicyComponent } from './private-labeling-policy.compon
|
||||
PolicyGridModule,
|
||||
MatExpansionModule,
|
||||
InfoSectionModule,
|
||||
HasFeaturePipeModule,
|
||||
],
|
||||
})
|
||||
export class PrivateLabelingPolicyModule {}
|
||||
|
@@ -3,39 +3,39 @@
|
||||
<p class="top-desc cnsl-secondary-text">{{'POLICY.DESCRIPTION' | translate}}</p>
|
||||
|
||||
<div class="tags" *ngIf="tags">
|
||||
<span class="tag" [ngClass]="{'active': tag === tagForFilter}"
|
||||
(click)="tagForFilter !== tag ? tagForFilter = tag : tagForFilter = ''" *ngFor="let tag of tags"><i
|
||||
class="las la-hashtag"></i>{{tag}}</span>
|
||||
<span class="tag" [ngClass]="{'active': tag === tagForFilter}"
|
||||
(click)="tagForFilter !== tag ? tagForFilter = tag : tagForFilter = ''" *ngFor="let tag of tags"><i
|
||||
class="las la-hashtag"></i>{{tag}}</span>
|
||||
</div>
|
||||
|
||||
<div class="row-lyt">
|
||||
<ng-container *ngFor="let policy of filteredPolicies">
|
||||
<ng-template cnslHasRole
|
||||
[hasRole]="type === PolicyComponentServiceType.ADMIN ? policy.iamWithRole : type === PolicyComponentServiceType.MGMT ? policy.orgWithRole : []">
|
||||
<div class="p-item card" @policy [attr.data-e2e]="'policy-card'">
|
||||
<div class="avatar {{policy.color}}">
|
||||
<mat-icon *ngIf="policy.svgIcon" class="icon" [svgIcon]="policy.svgIcon"></mat-icon>
|
||||
<i *ngIf="policy.icon" class="icon {{policy.icon}}"></i>
|
||||
</div>
|
||||
<div class="title">
|
||||
<span>{{policy.i18nTitle | translate}}</span>
|
||||
</div>
|
||||
<ng-container *ngFor="let policy of filteredPolicies">
|
||||
<ng-template cnslHasRole
|
||||
[hasRole]="type === PolicyComponentServiceType.ADMIN ? policy.iamWithRole : type === PolicyComponentServiceType.MGMT ? policy.orgWithRole : []">
|
||||
<div class="p-item card" @policy [attr.data-e2e]="'policy-card'">
|
||||
<div class="avatar {{policy.color}}">
|
||||
<mat-icon *ngIf="policy.svgIcon" class="mat-icon" [svgIcon]="policy.svgIcon"></mat-icon>
|
||||
<i *ngIf="policy.icon" class="icon {{policy.icon}}"></i>
|
||||
</div>
|
||||
<div class="title">
|
||||
<span>{{policy.i18nTitle | translate}}</span>
|
||||
</div>
|
||||
|
||||
<p class="desc cnsl-secondary-text">
|
||||
{{policy.i18nDesc | translate}}</p>
|
||||
<p class="desc cnsl-secondary-text">
|
||||
{{policy.i18nDesc | translate}}</p>
|
||||
|
||||
<span class="fill-space"></span>
|
||||
<div class="tags" *ngIf="policy.tags">
|
||||
<span class="tag cnsl-secondary-text" *ngFor="let tag of policy.tags"
|
||||
(click)="tagForFilter !== tag ? tagForFilter = tag : tagForFilter = ''"><i
|
||||
class="las la-hashtag"></i>{{tag}}</span>
|
||||
</div>
|
||||
<div class="btn-wrapper">
|
||||
<button
|
||||
[routerLink]="type === PolicyComponentServiceType.ADMIN ? policy.iamRouterLink : type === PolicyComponentServiceType.MGMT ? policy.orgRouterLink : null"
|
||||
mat-stroked-button>{{'POLICY.BTN_EDIT' | translate}}</button>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
<span class="fill-space"></span>
|
||||
<div class="tags" *ngIf="policy.tags">
|
||||
<span class="tag cnsl-secondary-text" *ngFor="let tag of policy.tags"
|
||||
(click)="tagForFilter !== tag ? tagForFilter = tag : tagForFilter = ''"><i
|
||||
class="las la-hashtag"></i>{{tag}}</span>
|
||||
</div>
|
||||
<div class="btn-wrapper">
|
||||
<button
|
||||
[routerLink]="type === PolicyComponentServiceType.ADMIN ? policy.iamRouterLink : type === PolicyComponentServiceType.MGMT ? policy.orgRouterLink : null"
|
||||
mat-stroked-button>{{'POLICY.BTN_EDIT' | translate}}</button>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
</div>
|
@@ -89,10 +89,15 @@ h2 {
|
||||
background: linear-gradient(40deg, #1f2937, #111827);
|
||||
}
|
||||
|
||||
.mat-icon {
|
||||
height: 2rem;
|
||||
width: 2rem;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.icon,
|
||||
i {
|
||||
font-size: 2.5rem;
|
||||
height: 2.5rem;
|
||||
line-height: 2.5rem;
|
||||
color: white;
|
||||
}
|
||||
|
@@ -6,28 +6,24 @@ import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
|
||||
import { HasFeaturePipeModule } from 'src/app/pipes/has-feature-pipe/has-feature-pipe.module';
|
||||
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module';
|
||||
|
||||
import { InfoSectionModule } from '../info-section/info-section.module';
|
||||
import { PolicyGridComponent } from './policy-grid.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [PolicyGridComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
HasRolePipeModule,
|
||||
HasRoleModule,
|
||||
TranslateModule,
|
||||
RouterModule,
|
||||
MatButtonModule,
|
||||
MatIconModule,
|
||||
MatTooltipModule,
|
||||
InfoSectionModule,
|
||||
HasFeaturePipeModule,
|
||||
],
|
||||
exports: [
|
||||
PolicyGridComponent,
|
||||
],
|
||||
declarations: [PolicyGridComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
HasRolePipeModule,
|
||||
HasRoleModule,
|
||||
TranslateModule,
|
||||
RouterModule,
|
||||
MatButtonModule,
|
||||
MatIconModule,
|
||||
MatTooltipModule,
|
||||
InfoSectionModule,
|
||||
],
|
||||
exports: [PolicyGridComponent],
|
||||
})
|
||||
export class PolicyGridModule { }
|
||||
export class PolicyGridModule {}
|
||||
|
@@ -1,22 +1,32 @@
|
||||
<div class="sidenav-container">
|
||||
<div class="sidenav-settings-list">
|
||||
<div class="sidenav-sticky-rel">
|
||||
<button *ngIf="currentSetting !== undefined" (click)="value = undefined"
|
||||
class="sidenav-setting-list-element mob-only" [ngClass]="{'active': true}">
|
||||
<button
|
||||
*ngIf="currentSetting !== undefined"
|
||||
(click)="value = undefined"
|
||||
class="sidenav-setting-list-element mob-only"
|
||||
[ngClass]="{ active: true }"
|
||||
>
|
||||
<i class="las la-angle-left"></i>
|
||||
<span>{{'USER.SETTINGS.TITLE' | translate}}</span>
|
||||
<span>{{ 'USER.SETTINGS.TITLE' | translate }}</span>
|
||||
</button>
|
||||
<ng-container *ngFor="let setting of settingsList">
|
||||
<ng-template *ngIf="setting.featureRequired, else btn" cnslHasFeature [hasFeature]="setting.featureRequired">
|
||||
<button (click)="value = setting.id" class="sidenav-setting-list-element hide-on-mobile"
|
||||
[ngClass]="{'active': currentSetting === setting.id, 'show': currentSetting === undefined}">
|
||||
<span>{{setting.i18nKey | translate}}</span>
|
||||
<ng-container *ngIf="setting.featureRequired; else btn">
|
||||
<button
|
||||
(click)="value = setting.id"
|
||||
class="sidenav-setting-list-element hide-on-mobile"
|
||||
[ngClass]="{ active: currentSetting === setting.id, show: currentSetting === undefined }"
|
||||
>
|
||||
<span>{{ setting.i18nKey | translate }}</span>
|
||||
</button>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
<ng-template #btn>
|
||||
<button (click)="value = setting.id" class="sidenav-setting-list-element hide-on-mobile"
|
||||
[ngClass]="{'active': currentSetting === setting.id, 'show': currentSetting === undefined}">
|
||||
<span>{{setting.i18nKey | translate}}</span>
|
||||
<button
|
||||
(click)="value = setting.id"
|
||||
class="sidenav-setting-list-element hide-on-mobile"
|
||||
[ngClass]="{ active: currentSetting === setting.id, show: currentSetting === undefined }"
|
||||
>
|
||||
<span>{{ setting.i18nKey | translate }}</span>
|
||||
</button>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
@@ -28,4 +38,4 @@
|
||||
<ng-content></ng-content>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -1,17 +1,23 @@
|
||||
<span class="title" mat-dialog-title>{{data.i18nTitle | translate}}</span>
|
||||
<span class="title" mat-dialog-title>{{ data.i18nTitle | translate }}</span>
|
||||
<div mat-dialog-content>
|
||||
<cnsl-project-roles-table class="role-table" *ngIf="projectId"
|
||||
[displayedColumns]="['select', 'key', 'displayname', 'group']" (changedSelection)="selectRoles($event)"
|
||||
[projectId]="projectId" [grantId]="grantId" [selectedKeys]="selectedRoleKeysList"
|
||||
[showSelectionActionButton]="false">
|
||||
<cnsl-project-roles-table
|
||||
class="role-table"
|
||||
*ngIf="projectId"
|
||||
[displayedColumns]="['select', 'key', 'displayname', 'group']"
|
||||
(changedSelection)="selectRoles($event)"
|
||||
[projectId]="projectId"
|
||||
[grantId]="grantId"
|
||||
[selectedKeys]="selectedRoleKeysList"
|
||||
[showSelectionActionButton]="false"
|
||||
>
|
||||
</cnsl-project-roles-table>
|
||||
</div>
|
||||
<div mat-dialog-actions class="action">
|
||||
<button mat-stroked-button (click)="closeDialog()">
|
||||
{{'ACTIONS.CANCEL' | translate}}
|
||||
{{ 'ACTIONS.CANCEL' | translate }}
|
||||
</button>
|
||||
<span class="fill-space"></span>
|
||||
<button mat-raised-button color="primary" class="ok-button" (click)="closeDialogWithSuccess()">
|
||||
{{'ACTIONS.CHANGE' | translate}}
|
||||
{{ 'ACTIONS.CHANGE' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -1,14 +0,0 @@
|
||||
<div *ngIf="features" class="tier card">
|
||||
<img class="logo dark" alt="zitadel logo" src="../assets/images/zitadel-logo-solo-light-icon.svg" />
|
||||
<img alt="zitadel logo" class="logo light" src="../assets/images/zitadel-logo-solo-dark-icon.svg" />
|
||||
<div class="tier-text" *ngIf="features.tier">
|
||||
<p class="title"><strong>{{features.tier.name}}</strong></p>
|
||||
<p>{{features.tier?.description}}</p>
|
||||
<p class="state" [ngClass]="{'active': features.tier.state === FeaturesState.FEATURES_STATE_ACTIVE}">
|
||||
{{'FEATURES.TIERSTATES.'+features.tier.state | translate}}</p>
|
||||
<p>{{features.tier?.statusInfo}}</p>
|
||||
</div>
|
||||
<span class="fill-space"></span>
|
||||
<button mat-raised-button color="primary"
|
||||
[routerLink]="[ iam ? '/system/features' : '/org/features']">{{'FEATURES.BTN-EDIT' | translate}}</button>
|
||||
</div>
|
@@ -1,59 +0,0 @@
|
||||
@mixin tier-theme($theme) {
|
||||
$is-dark-theme: map-get($theme, is-dark);
|
||||
$background: map-get($theme, background);
|
||||
|
||||
.tier {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
padding: 1rem;
|
||||
border-radius: 0.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
|
||||
.ext {
|
||||
margin-right: 0.5rem;
|
||||
align-self: center;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.logo {
|
||||
margin-right: 1rem;
|
||||
max-height: 40px;
|
||||
object-fit: contain;
|
||||
filter: grayscale(1);
|
||||
transition: filter 0.3s ease;
|
||||
|
||||
&.dark {
|
||||
display: if($is-dark-theme, inline-block, none);
|
||||
}
|
||||
|
||||
&.light {
|
||||
display: if($is-dark-theme, none, inline-block);
|
||||
}
|
||||
}
|
||||
|
||||
.tier-text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
|
||||
p:not(.state) {
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 16px;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.fill-space {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
button {
|
||||
// min-width: 200px;
|
||||
align-self: center;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,25 +0,0 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ZitadelTierComponent } from './zitadel-tier.component';
|
||||
|
||||
describe('ZitadelTierComponent', () => {
|
||||
let component: ZitadelTierComponent;
|
||||
let fixture: ComponentFixture<ZitadelTierComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ZitadelTierComponent],
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ZitadelTierComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@@ -1,14 +0,0 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { Features, FeaturesState } from 'src/app/proto/generated/zitadel/features_pb';
|
||||
|
||||
@Component({
|
||||
selector: 'cnsl-zitadel-tier',
|
||||
templateUrl: './zitadel-tier.component.html',
|
||||
styleUrls: ['./zitadel-tier.component.scss'],
|
||||
})
|
||||
export class ZitadelTierComponent {
|
||||
@Input() public features!: Features.AsObject;
|
||||
@Input() public iam: boolean = false;
|
||||
|
||||
FeaturesState: any = FeaturesState;
|
||||
}
|
@@ -1,31 +0,0 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
|
||||
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module';
|
||||
|
||||
import { ZitadelTierComponent } from './zitadel-tier.component';
|
||||
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
ZitadelTierComponent,
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
RouterModule,
|
||||
MatButtonModule,
|
||||
MatIconModule,
|
||||
HasRoleModule,
|
||||
HasRolePipeModule,
|
||||
TranslateModule,
|
||||
],
|
||||
exports: [
|
||||
ZitadelTierComponent,
|
||||
],
|
||||
})
|
||||
export class ZitadelTierModule { }
|
@@ -10,9 +10,9 @@ import { PaginatorComponent } from 'src/app/modules/paginator/paginator.componen
|
||||
import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component';
|
||||
import { Action, ActionState } from 'src/app/proto/generated/zitadel/action_pb';
|
||||
import {
|
||||
CreateActionRequest,
|
||||
ListActionsResponse,
|
||||
UpdateActionRequest,
|
||||
CreateActionRequest,
|
||||
ListActionsResponse,
|
||||
UpdateActionRequest,
|
||||
} from 'src/app/proto/generated/zitadel/management_pb';
|
||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
@@ -49,7 +49,7 @@ export class ActionTableComponent implements OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
public ngOnInit(): void {
|
||||
ngOnInit(): void {
|
||||
this.getData(10, 0);
|
||||
}
|
||||
|
||||
|
@@ -1,80 +1,84 @@
|
||||
<div class="max-width-container">
|
||||
<div class="enlarged-container">
|
||||
<h1>{{ 'FLOWS.TITLE' | translate }}</h1>
|
||||
<p class="desc cnsl-secondary-text">{{'FLOWS.DESCRIPTION' | translate }}</p>
|
||||
<p class="desc cnsl-secondary-text">{{ 'FLOWS.DESCRIPTION' | translate }}</p>
|
||||
|
||||
<cnsl-info-section class=" max-actions" *ngIf="maxActions">{{'FLOWS.ACTIONSMAX' | translate: ({value: maxActions})
|
||||
}}
|
||||
<cnsl-info-section class="max-actions" *ngIf="maxActions"
|
||||
>{{ 'FLOWS.ACTIONSMAX' | translate: { value: maxActions } }}
|
||||
</cnsl-info-section>
|
||||
|
||||
<cnsl-info-section *ngIf="(['actions'] | hasFeature | async) === false" [featureLink]="['/org/features']"
|
||||
class="info" [type]="InfoSectionType.WARN">
|
||||
<span [innerHTML]="'FEATURES.NOTAVAILABLE' | translate: ({value: 'actions'})"></span>
|
||||
</cnsl-info-section>
|
||||
|
||||
<ng-template cnslHasRole [hasRole]="[ 'org.action.read']">
|
||||
<cnsl-card title="{{'FLOWS.ACTIONSTITLE' | translate}}" description="{{'FLOWS.ACTIONSDESCRIPTION' | translate}}">
|
||||
<ng-template cnslHasRole [hasRole]="['org.action.read']">
|
||||
<cnsl-card title="{{ 'FLOWS.ACTIONSTITLE' | translate }}" description="{{ 'FLOWS.ACTIONSDESCRIPTION' | translate }}">
|
||||
<cnsl-action-table (changedSelection)="selection = $event"></cnsl-action-table>
|
||||
</cnsl-card>
|
||||
</ng-template>
|
||||
|
||||
<div class="title-section">
|
||||
<h2>{{'FLOWS.FLOWSTITLE' | translate}}</h2>
|
||||
<h2>{{ 'FLOWS.FLOWSTITLE' | translate }}</h2>
|
||||
<i class="las la-exchange-alt"></i>
|
||||
</div>
|
||||
|
||||
<p class="desc cnsl-secondary-text">{{'FLOWS.FLOWSDESCRIPTION' | translate}}</p>
|
||||
<p class="desc cnsl-secondary-text">{{ 'FLOWS.FLOWSDESCRIPTION' | translate }}</p>
|
||||
|
||||
<ng-template cnslHasRole [hasRole]="[ 'org.flow.read']">
|
||||
<ng-template cnslHasRole [hasRole]="['org.flow.read']">
|
||||
<div *ngIf="flow" class="flow">
|
||||
<cnsl-form-field class="formfield" appearance="outline">
|
||||
<cnsl-label>{{ 'FLOWS.FLOWTYPE' | translate }}</cnsl-label>
|
||||
<mat-select [formControl]="typeControl">
|
||||
<mat-option *ngFor="let type of typesForSelection" [value]="type">
|
||||
{{ 'FLOWS.TYPES.'+type | translate }}
|
||||
{{ 'FLOWS.TYPES.' + type | translate }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</cnsl-form-field>
|
||||
|
||||
|
||||
<div *ngIf="flow" class="trigger-wrapper">
|
||||
<div class="topbottomline"></div>
|
||||
|
||||
<div class="flow-type">
|
||||
<i class="type-icon las la-dot-circle"></i>
|
||||
<span>{{'FLOWS.TYPES.'+flow.type | translate}}</span>
|
||||
<button matTooltip="{{'ACTIONS.CLEAR' | translate}}" mat-icon-button color="warn" (click)="clearFlow()"><i
|
||||
class="type-button-icon las la-trash"></i></button>
|
||||
<span>{{ 'FLOWS.TYPES.' + flow.type | translate }}</span>
|
||||
<button matTooltip="{{ 'ACTIONS.CLEAR' | translate }}" mat-icon-button color="warn" (click)="clearFlow()">
|
||||
<i class="type-button-icon las la-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<cnsl-card *ngFor="let trigger of flow.triggerActionsList; index as i" class="trigger">
|
||||
<div class="trigger-top">
|
||||
<mat-icon svgIcon="mdi_arrow_right_bottom" class="icon"></mat-icon>
|
||||
<span>{{'FLOWS.TRIGGERTYPES.'+trigger.triggerType | translate}}</span>
|
||||
<span>{{ 'FLOWS.TRIGGERTYPES.' + trigger.triggerType | translate }}</span>
|
||||
</div>
|
||||
<span class="fill-space"></span>
|
||||
<div class="flow-action-wrapper" cdkDropList (cdkDropListDropped)="drop(i, trigger.actionsList, $event)">
|
||||
<div cdkDrag cdkDragLockAxis="y" cdkDragBoundary=".action-wrapper" class="flow-action"
|
||||
*ngFor="let action of trigger.actionsList">
|
||||
<div
|
||||
cdkDrag
|
||||
cdkDragLockAxis="y"
|
||||
cdkDragBoundary=".action-wrapper"
|
||||
class="flow-action"
|
||||
*ngFor="let action of trigger.actionsList"
|
||||
>
|
||||
<i class="las la-code"></i>
|
||||
<span class="flow-action-name">{{action.name}}</span>
|
||||
<span class="flow-action-name">{{ action.name }}</span>
|
||||
<span class="fill-space"></span>
|
||||
<span class="state"
|
||||
[ngClass]="{'active': action.state === ActionState.ACTION_STATE_ACTIVE,'inactive': action.state === ActionState.ACTION_STATE_INACTIVE }">
|
||||
{{'FLOWS.STATES.'+action.state | translate}}</span>
|
||||
<span
|
||||
class="state"
|
||||
[ngClass]="{
|
||||
active: action.state === ActionState.ACTION_STATE_ACTIVE,
|
||||
inactive: action.state === ActionState.ACTION_STATE_INACTIVE
|
||||
}"
|
||||
>
|
||||
{{ 'FLOWS.STATES.' + action.state | translate }}</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</cnsl-card>
|
||||
|
||||
|
||||
<button class="add-btn cnsl-action-button" mat-raised-button color="primary" (click)="openAddTrigger()">
|
||||
<mat-icon>add</mat-icon>
|
||||
<span>{{'FLOWS.ADDTRIGGER' | translate}}</span>
|
||||
<span *ngIf="selection && selection.length"> ({{selection.length}})</span>
|
||||
<span>{{ 'FLOWS.ADDTRIGGER' | translate }}</span>
|
||||
<span *ngIf="selection && selection.length"> ({{ selection.length }})</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</ng-template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -22,7 +22,6 @@ import { RefreshTableModule } from 'src/app/modules/refresh-table/refresh-table.
|
||||
import { TableActionsModule } from 'src/app/modules/table-actions/table-actions.module';
|
||||
import { WarnDialogModule } from 'src/app/modules/warn-dialog/warn-dialog.module';
|
||||
import { DurationToSecondsPipeModule } from 'src/app/pipes/duration-to-seconds-pipe/duration-to-seconds-pipe.module';
|
||||
import { HasFeaturePipeModule } from 'src/app/pipes/has-feature-pipe/has-feature-pipe.module';
|
||||
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module';
|
||||
import { LocalizedDatePipeModule } from 'src/app/pipes/localized-date-pipe/localized-date-pipe.module';
|
||||
import { TimestampToDatePipeModule } from 'src/app/pipes/timestamp-to-date-pipe/timestamp-to-date-pipe.module';
|
||||
@@ -63,7 +62,6 @@ import { AddFlowDialogComponent } from './add-flow-dialog/add-flow-dialog.compon
|
||||
InfoSectionModule,
|
||||
HasRolePipeModule,
|
||||
TableActionsModule,
|
||||
HasFeaturePipeModule,
|
||||
CodemirrorModule,
|
||||
],
|
||||
})
|
||||
|
@@ -1,51 +1,69 @@
|
||||
<div class="max-width-container">
|
||||
<ng-container *ngIf="(['org.write$'] | hasRole) as canwrite$">
|
||||
<ng-container *ngIf="['org.write$'] | hasRole as canwrite$">
|
||||
<div class="domain-top-view">
|
||||
<div>
|
||||
<div class="domain-title-row">
|
||||
<h1>{{ 'ORG.DOMAINS.TITLE' | translate }}</h1>
|
||||
<a mat-icon-button
|
||||
<a
|
||||
mat-icon-button
|
||||
href="https://docs.zitadel.ch/docs/guides/basics/organizations#how-zitadel-handles-usernames"
|
||||
rel="noreferrer" target="_blank">
|
||||
rel="noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
<i class="las la-info-circle"></i>
|
||||
</a>
|
||||
</div>
|
||||
<p class="desc cnsl-secondary-text">{{ 'ORG.DOMAINS.DESCRIPTION' | translate }}</p>
|
||||
</div>
|
||||
<span class=" fill-space"></span>
|
||||
<span class="fill-space"></span>
|
||||
|
||||
<button [disabled]="(canwrite$ | async) === false || (['custom_domain'] | hasFeature | async) === false"
|
||||
matTooltip="Add domain" mat-raised-button color="primary" class="cnsl-action-button" (click)="addNewDomain()">
|
||||
<button
|
||||
[disabled]="(canwrite$ | async) === false"
|
||||
matTooltip="Add domain"
|
||||
mat-raised-button
|
||||
color="primary"
|
||||
class="cnsl-action-button"
|
||||
(click)="addNewDomain()"
|
||||
>
|
||||
<mat-icon>add</mat-icon>
|
||||
<span>{{'ACTIONS.NEW' | translate}}</span>
|
||||
<cnsl-action-keys (actionTriggered)="addNewDomain()">
|
||||
</cnsl-action-keys>
|
||||
<span>{{ 'ACTIONS.NEW' | translate }}</span>
|
||||
<cnsl-action-keys (actionTriggered)="addNewDomain()"> </cnsl-action-keys>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<cnsl-info-section *ngIf="(['custom_domain'] | hasFeature | async) === false" [featureLink]="['/org/features']"
|
||||
class="info" [type]="InfoSectionType.WARN">
|
||||
<span [innerHTML]="'FEATURES.NOTAVAILABLE' | translate: ({value: 'custom_domain'})"></span>
|
||||
</cnsl-info-section>
|
||||
|
||||
<cnsl-card *ngFor="let domain of domains" class="domain-card">
|
||||
<div class="domain">
|
||||
<span class="title">{{domain.domainName}}</span>
|
||||
<span class="title">{{ domain.domainName }}</span>
|
||||
|
||||
<i matTooltip="verified" *ngIf="domain.isVerified" class="verified las la-check-circle"></i>
|
||||
<i matTooltip="primary" *ngIf="domain.isPrimary" class="primary las la-star"></i>
|
||||
<a *ngIf="domain.isVerified && !domain.isPrimary && (canwrite$ | async)" class="primaryset"
|
||||
(click)="setPrimary(domain)">{{'ORG.DOMAINS.SETPRIMARY' | translate}}</a>
|
||||
<a
|
||||
*ngIf="domain.isVerified && !domain.isPrimary && (canwrite$ | async)"
|
||||
class="primaryset"
|
||||
(click)="setPrimary(domain)"
|
||||
>{{ 'ORG.DOMAINS.SETPRIMARY' | translate }}</a
|
||||
>
|
||||
|
||||
<span class="fill-space"></span>
|
||||
<button mat-icon-button [disabled]="(canwrite$ | async) === false || domain.isVerified"
|
||||
*ngIf="(canwrite$ | async) && (['custom_domain'] | hasFeature | async)" (click)="verifyDomain(domain)">
|
||||
<button
|
||||
mat-icon-button
|
||||
[disabled]="(canwrite$ | async) === false || domain.isVerified"
|
||||
*ngIf="canwrite$ | async"
|
||||
(click)="verifyDomain(domain)"
|
||||
>
|
||||
<i class="las la-pen"></i>
|
||||
</button>
|
||||
<button class="domain-rem-button" [disabled]="(canwrite$ | async) === false || domain.isPrimary"
|
||||
matTooltip="Remove domain" color="warn" mat-icon-button (click)="removeDomain(domain.domainName)"><i
|
||||
class="las la-trash"></i></button>
|
||||
<button
|
||||
class="domain-rem-button"
|
||||
[disabled]="(canwrite$ | async) === false || domain.isPrimary"
|
||||
matTooltip="Remove domain"
|
||||
color="warn"
|
||||
mat-icon-button
|
||||
(click)="removeDomain(domain.domainName)"
|
||||
>
|
||||
<i class="las la-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
</cnsl-card>
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -11,7 +11,6 @@ import { CardModule } from 'src/app/modules/card/card.module';
|
||||
import { InfoSectionModule } from 'src/app/modules/info-section/info-section.module';
|
||||
import { InputModule } from 'src/app/modules/input/input.module';
|
||||
import { SharedModule } from 'src/app/modules/shared/shared.module';
|
||||
import { HasFeaturePipeModule } from 'src/app/pipes/has-feature-pipe/has-feature-pipe.module';
|
||||
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module';
|
||||
|
||||
import { AddDomainDialogModule } from './add-domain-dialog/add-domain-dialog.module';
|
||||
@@ -28,7 +27,6 @@ import { DomainsComponent } from './domains.component';
|
||||
CommonModule,
|
||||
MatIconModule,
|
||||
CardModule,
|
||||
HasFeaturePipeModule,
|
||||
HasRolePipeModule,
|
||||
ActionKeysModule,
|
||||
InfoSectionModule,
|
||||
|
@@ -15,7 +15,6 @@ import { PaginatorModule } from 'src/app/modules/paginator/paginator.module';
|
||||
import { RefreshTableModule } from 'src/app/modules/refresh-table/refresh-table.module';
|
||||
import { SharedModule } from 'src/app/modules/shared/shared.module';
|
||||
import { TableActionsModule } from 'src/app/modules/table-actions/table-actions.module';
|
||||
import { HasFeaturePipeModule } from 'src/app/pipes/has-feature-pipe/has-feature-pipe.module';
|
||||
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module';
|
||||
import { LocalizedDatePipeModule } from 'src/app/pipes/localized-date-pipe/localized-date-pipe.module';
|
||||
import { TimestampToDatePipeModule } from 'src/app/pipes/timestamp-to-date-pipe/timestamp-to-date-pipe.module';
|
||||
@@ -32,7 +31,6 @@ import { FailedEventsComponent } from './failed-events.component';
|
||||
TableActionsModule,
|
||||
MatIconModule,
|
||||
CardModule,
|
||||
HasFeaturePipeModule,
|
||||
HasRolePipeModule,
|
||||
PaginatorModule,
|
||||
MatButtonModule,
|
||||
|
@@ -1,12 +1,12 @@
|
||||
<div class="max-width-container">
|
||||
<div class="home-wrapper enlarged-container">
|
||||
<div class="header">
|
||||
<h1 class="title">{{'HOME.WELCOME' | translate}}</h1>
|
||||
<h1 class="title">{{ 'HOME.WELCOME' | translate }}</h1>
|
||||
</div>
|
||||
|
||||
<cnsl-shortcuts></cnsl-shortcuts>
|
||||
|
||||
<p class="disclaimer cnsl-secondary-text">{{'HOME.DISCLAIMER' | translate}} </p>
|
||||
<p class="disclaimer cnsl-secondary-text">{{ 'HOME.DISCLAIMER' | translate }}</p>
|
||||
|
||||
<span class="fill-space"></span>
|
||||
<div class="home-grid-container">
|
||||
@@ -14,24 +14,32 @@
|
||||
<div class="grid-item-avatar blue">
|
||||
<i class="icon las la-file-alt"></i>
|
||||
</div>
|
||||
<span>{{'HOME.DOCUMENTATION.TITLE' | translate}}</span>
|
||||
<span>{{ 'HOME.DOCUMENTATION.TITLE' | translate }}</span>
|
||||
</a>
|
||||
|
||||
<a href="https://docs.zitadel.ch/docs/guides/basics/get-started/" target="_blank" rel="noreferrer"
|
||||
class="grid-item green">
|
||||
<a
|
||||
href="https://docs.zitadel.ch/docs/guides/basics/get-started/"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
class="grid-item green"
|
||||
>
|
||||
<div class="grid-item-avatar green">
|
||||
<i class="icon las la-play"></i>
|
||||
</div>
|
||||
<span>{{'HOME.GETSTARTED.TITLE' | translate}}</span>
|
||||
<span>{{ 'HOME.GETSTARTED.TITLE' | translate }}</span>
|
||||
</a>
|
||||
|
||||
<a href="https://docs.zitadel.ch/docs/quickstarts/introduction" target="_blank" rel="noreferrer"
|
||||
class="grid-item green">
|
||||
<a
|
||||
href="https://docs.zitadel.ch/docs/quickstarts/introduction"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
class="grid-item green"
|
||||
>
|
||||
<div class="grid-item-avatar green">
|
||||
<i class="icon las la-play"></i>
|
||||
</div>
|
||||
<span>{{'HOME.QUICKSTARTS.TITLE' | translate}}</span>
|
||||
<span>{{ 'HOME.QUICKSTARTS.TITLE' | translate }}</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -15,7 +15,6 @@ import { PaginatorModule } from 'src/app/modules/paginator/paginator.module';
|
||||
import { RefreshTableModule } from 'src/app/modules/refresh-table/refresh-table.module';
|
||||
import { SharedModule } from 'src/app/modules/shared/shared.module';
|
||||
import { TableActionsModule } from 'src/app/modules/table-actions/table-actions.module';
|
||||
import { HasFeaturePipeModule } from 'src/app/pipes/has-feature-pipe/has-feature-pipe.module';
|
||||
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module';
|
||||
import { LocalizedDatePipeModule } from 'src/app/pipes/localized-date-pipe/localized-date-pipe.module';
|
||||
import { TimestampToDatePipeModule } from 'src/app/pipes/timestamp-to-date-pipe/timestamp-to-date-pipe.module';
|
||||
@@ -32,7 +31,6 @@ import { IamViewsComponent } from './iam-views.component';
|
||||
TableActionsModule,
|
||||
MatIconModule,
|
||||
CardModule,
|
||||
HasFeaturePipeModule,
|
||||
HasRolePipeModule,
|
||||
MatButtonModule,
|
||||
CopyToClipboardModule,
|
||||
@@ -48,5 +46,6 @@ import { IamViewsComponent } from './iam-views.component';
|
||||
MatTableModule,
|
||||
MatSortModule,
|
||||
],
|
||||
exports: [],
|
||||
})
|
||||
export class IamViewsModule {}
|
||||
|
@@ -2,7 +2,6 @@ import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { AuthGuard } from 'src/app/guards/auth.guard';
|
||||
import { RoleGuard } from 'src/app/guards/role.guard';
|
||||
import { FeatureServiceType } from 'src/app/modules/features/features.component';
|
||||
import { PolicyComponentServiceType, PolicyComponentType } from 'src/app/modules/policies/policy-component-types.enum';
|
||||
|
||||
import { IamComponent } from './iam.component';
|
||||
@@ -24,14 +23,6 @@ const routes: Routes = [
|
||||
roles: ['iam.member.read'],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'features',
|
||||
loadChildren: () => import('src/app/modules/features/features.module').then((m) => m.FeaturesModule),
|
||||
data: {
|
||||
roles: ['iam.features.read'],
|
||||
serviceType: FeatureServiceType.ADMIN,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'idp',
|
||||
children: [
|
||||
|
@@ -3,23 +3,26 @@
|
||||
<div class="iam-top-row">
|
||||
<div>
|
||||
<div class="iam-title-row">
|
||||
<h1 class="iam-title">{{'IAM.POLICIES.TITLE' | translate}}</h1>
|
||||
<h1 class="iam-title">{{ 'IAM.POLICIES.TITLE' | translate }}</h1>
|
||||
</div>
|
||||
<p class="iam-sub cnsl-secondary-text">{{'IAM.POLICIES.DESCRIPTION' | translate}}</p>
|
||||
<p class="iam-sub cnsl-secondary-text">{{ 'IAM.POLICIES.DESCRIPTION' | translate }}</p>
|
||||
</div>
|
||||
<span class="fill-space"></span>
|
||||
<cnsl-contributors [totalResult]="totalMemberResult" [loading]="loading$ | async"
|
||||
[membersSubject]="membersSubject" title="{{ 'PROJECT.MEMBER.TITLE' | translate }}"
|
||||
description="{{ 'PROJECT.MEMBER.TITLEDESC' | translate }}" (addClicked)="openAddMember()"
|
||||
(showDetailClicked)="showDetail()" (refreshClicked)="loadMembers()" [disabled]="false">
|
||||
<cnsl-contributors
|
||||
[totalResult]="totalMemberResult"
|
||||
[loading]="loading$ | async"
|
||||
[membersSubject]="membersSubject"
|
||||
title="{{ 'PROJECT.MEMBER.TITLE' | translate }}"
|
||||
description="{{ 'PROJECT.MEMBER.TITLEDESC' | translate }}"
|
||||
(addClicked)="openAddMember()"
|
||||
(showDetailClicked)="showDetail()"
|
||||
(refreshClicked)="loadMembers()"
|
||||
[disabled]="false"
|
||||
>
|
||||
</cnsl-contributors>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="max-width-container">
|
||||
<h1 class="iam-subtitle">{{'FEATURES.TITLE' | translate}}</h1>
|
||||
<p class="iam-top-desc cnsl-secondary-text">{{'FEATURES.DESCRIPTION' | translate}} </p>
|
||||
<cnsl-zitadel-tier [features]="features" [iam]="true"></cnsl-zitadel-tier>
|
||||
|
||||
<cnsl-policy-grid class="policies" [type]="PolicyComponentServiceType.ADMIN"></cnsl-policy-grid>
|
||||
</div>
|
||||
<cnsl-policy-grid [type]="PolicyComponentServiceType.ADMIN"></cnsl-policy-grid>
|
||||
</div>
|
||||
|
@@ -52,8 +52,3 @@
|
||||
text-transform: uppercase;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.policies {
|
||||
display: block;
|
||||
padding-top: 2rem;
|
||||
}
|
||||
|
@@ -21,7 +21,6 @@ import { MetaLayoutModule } from 'src/app/modules/meta-layout/meta-layout.module
|
||||
import { PolicyGridModule } from 'src/app/modules/policy-grid/policy-grid.module';
|
||||
import { RefreshTableModule } from 'src/app/modules/refresh-table/refresh-table.module';
|
||||
import { SharedModule } from 'src/app/modules/shared/shared.module';
|
||||
import { ZitadelTierModule } from 'src/app/modules/zitadel-tier/zitadel-tier.module';
|
||||
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module';
|
||||
import { LocalizedDatePipeModule } from 'src/app/pipes/localized-date-pipe/localized-date-pipe.module';
|
||||
import { TimestampToDatePipeModule } from 'src/app/pipes/timestamp-to-date-pipe/timestamp-to-date-pipe.module';
|
||||
@@ -44,7 +43,6 @@ import { IamComponent } from './iam.component';
|
||||
MetaLayoutModule,
|
||||
MatIconModule,
|
||||
MatTableModule,
|
||||
ZitadelTierModule,
|
||||
InputModule,
|
||||
MatSortModule,
|
||||
MatTooltipModule,
|
||||
|
@@ -10,14 +10,19 @@
|
||||
|
||||
<div class="org-create-main-content">
|
||||
<ng-template cnslHasRole [hasRole]="['iam.write']">
|
||||
<mat-slide-toggle [disabled]="currentCreateStep !== 1" class="example-margin" color="primary"
|
||||
(change)="changeSelf($event)" [(ngModel)]="forSelf">
|
||||
{{'ORG.PAGES.USERSELFACCOUNT' | translate}}
|
||||
<mat-slide-toggle
|
||||
[disabled]="currentCreateStep !== 1"
|
||||
class="example-margin"
|
||||
color="primary"
|
||||
(change)="changeSelf($event)"
|
||||
[(ngModel)]="forSelf"
|
||||
>
|
||||
{{ 'ORG.PAGES.USERSELFACCOUNT' | translate }}
|
||||
</mat-slide-toggle>
|
||||
|
||||
<ng-container *ngIf="!forSelf">
|
||||
<ng-container *ngIf="currentCreateStep === 1">
|
||||
<h1>{{'ORG.PAGES.ORGDETAIL_TITLE' | translate}} </h1>
|
||||
<h1>{{ 'ORG.PAGES.ORGDETAIL_TITLE' | translate }}</h1>
|
||||
|
||||
<form [formGroup]="orgForm" (ngSubmit)="next()">
|
||||
<div class="content">
|
||||
@@ -33,16 +38,22 @@
|
||||
|
||||
<div class="btn-container">
|
||||
<span class="fill-space"></span>
|
||||
<button [disabled]="orgForm.invalid" color="primary" mat-raised-button class="big-button"
|
||||
cdkFocusInitial type="submit">
|
||||
{{'ACTIONS.CONTINUE' | translate}}
|
||||
<button
|
||||
[disabled]="orgForm.invalid"
|
||||
color="primary"
|
||||
mat-raised-button
|
||||
class="big-button"
|
||||
cdkFocusInitial
|
||||
type="submit"
|
||||
>
|
||||
{{ 'ACTIONS.CONTINUE' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="currentCreateStep === createSteps">
|
||||
<h1>{{'ORG.PAGES.ORGDETAILUSER_TITLE' | translate}}</h1>
|
||||
<h1>{{ 'ORG.PAGES.ORGDETAILUSER_TITLE' | translate }}</h1>
|
||||
|
||||
<div class="user">
|
||||
<form [formGroup]="userForm" class="form">
|
||||
@@ -90,7 +101,7 @@
|
||||
<cnsl-label>{{ 'USER.PROFILE.GENDER' | translate }}</cnsl-label>
|
||||
<mat-select formControlName="gender">
|
||||
<mat-option *ngFor="let gender of genders" [value]="gender">
|
||||
{{ 'GENDERS.'+gender | translate }}
|
||||
{{ 'GENDERS.' + gender | translate }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
<span cnslError *ngIf="gender?.invalid && gender?.errors?.required">
|
||||
@@ -101,7 +112,7 @@
|
||||
<cnsl-label>{{ 'USER.PROFILE.PREFERRED_LANGUAGE' | translate }}</cnsl-label>
|
||||
<mat-select formControlName="preferredLanguage">
|
||||
<mat-option *ngFor="let language of languages" [value]="language">
|
||||
{{ 'LANGUAGES.'+language | translate }}
|
||||
{{ 'LANGUAGES.' + language | translate }}
|
||||
</mat-option>
|
||||
<span cnslError *ngIf="preferredLanguage?.invalid && preferredLanguage?.errors?.required">
|
||||
{{ 'USER.VALIDATION.REQUIRED' | translate }}
|
||||
@@ -109,9 +120,14 @@
|
||||
</mat-select>
|
||||
</cnsl-form-field>
|
||||
|
||||
<mat-checkbox class="checkbox" [(ngModel)]="usePassword" [ngModelOptions]="{standalone: true}"
|
||||
(change)="initPwdValidators()">
|
||||
{{'ORG.PAGES.USEPASSWORD' | translate}}</mat-checkbox>
|
||||
<mat-checkbox
|
||||
class="checkbox"
|
||||
[(ngModel)]="usePassword"
|
||||
[ngModelOptions]="{ standalone: true }"
|
||||
(change)="initPwdValidators()"
|
||||
>
|
||||
{{ 'ORG.PAGES.USEPASSWORD' | translate }}</mat-checkbox
|
||||
>
|
||||
|
||||
<ng-container *ngIf="usePassword && pwdForm">
|
||||
<p class="section cnsl-secondary-text">{{ 'USER.CREATE.PASSWORDSECTION' | translate }}</p>
|
||||
@@ -122,18 +138,27 @@
|
||||
<form [formGroup]="pwdForm" class="pwd-form">
|
||||
<cnsl-form-field class="pwd" *ngIf="password" appearance="outline">
|
||||
<cnsl-label>{{ 'USER.PASSWORD.NEW' | translate }}</cnsl-label>
|
||||
<input cnslInput autocomplete="off" name="firstpassword" formControlName="password"
|
||||
type="password" />
|
||||
<input
|
||||
cnslInput
|
||||
autocomplete="off"
|
||||
name="firstpassword"
|
||||
formControlName="password"
|
||||
type="password"
|
||||
/>
|
||||
|
||||
<span cnslError *ngIf="password?.errors?.required">
|
||||
{{ 'USER.VALIDATION.REQUIRED' | translate }}
|
||||
</span>
|
||||
|
||||
</cnsl-form-field>
|
||||
<cnsl-form-field class="pwd" *ngIf="confirmPassword" appearance="outline">
|
||||
<cnsl-label>{{ 'USER.PASSWORD.CONFIRM' | translate }}</cnsl-label>
|
||||
<input cnslInput autocomplete="off" name="confirmPassword" formControlName="confirmPassword"
|
||||
type="password" />
|
||||
<input
|
||||
cnslInput
|
||||
autocomplete="off"
|
||||
name="confirmPassword"
|
||||
formControlName="confirmPassword"
|
||||
type="password"
|
||||
/>
|
||||
|
||||
<span cnslError *ngIf="confirmPassword?.errors?.required">
|
||||
{{ 'USER.VALIDATION.REQUIRED' | translate }}
|
||||
@@ -146,12 +171,19 @@
|
||||
</ng-container>
|
||||
</div>
|
||||
<div class="btn-container">
|
||||
<button color="primary" class="small-button" type="button" (click)="previous()" mat-stroked-button>{{
|
||||
'ACTIONS.BACK' | translate }}</button>
|
||||
<button color="primary" class="small-button" type="button" (click)="previous()" mat-stroked-button>
|
||||
{{ 'ACTIONS.BACK' | translate }}
|
||||
</button>
|
||||
<span class="fill-space"></span>
|
||||
<button color="primary" class="big-button" (click)="finish()"
|
||||
[disabled]="orgForm.invalid || userForm.invalid || ((usePassword && pwdForm) ? pwdForm?.invalid : false)"
|
||||
mat-raised-button>{{ 'ACTIONS.FINISH' | translate }}</button>
|
||||
<button
|
||||
color="primary"
|
||||
class="big-button"
|
||||
(click)="finish()"
|
||||
[disabled]="orgForm.invalid || userForm.invalid || (usePassword && pwdForm ? pwdForm?.invalid : false)"
|
||||
mat-raised-button
|
||||
>
|
||||
{{ 'ACTIONS.FINISH' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@@ -161,7 +193,7 @@
|
||||
<ng-template cnslHasRole [hasRole]="['org.create']">
|
||||
<div *ngIf="forSelf">
|
||||
<ng-container *ngIf="currentCreateStep === 1">
|
||||
<h1>{{'ORG.PAGES.ORGDETAIL_TITLE_WITHOUT_DOMAIN' | translate}} </h1>
|
||||
<h1>{{ 'ORG.PAGES.ORGDETAIL_TITLE_WITHOUT_DOMAIN' | translate }}</h1>
|
||||
|
||||
<form [formGroup]="orgForm" (ngSubmit)="createOrgForSelf()">
|
||||
<div class="content">
|
||||
@@ -173,9 +205,15 @@
|
||||
|
||||
<div class="btn-container">
|
||||
<span class="fill-space"></span>
|
||||
<button [disabled]="orgForm.invalid" color="primary" mat-raised-button class="big-button"
|
||||
cdkFocusInitial type="submit">
|
||||
{{'CREATE' | translate}}
|
||||
<button
|
||||
[disabled]="orgForm.invalid"
|
||||
color="primary"
|
||||
mat-raised-button
|
||||
class="big-button"
|
||||
cdkFocusInitial
|
||||
type="submit"
|
||||
>
|
||||
{{ 'ACTIONS.CREATE' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
@@ -184,4 +222,4 @@
|
||||
</ng-template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -1,28 +1,35 @@
|
||||
<cnsl-top-view [hasBackButton]="false" title="{{org?.name}}" [isActive]="org?.state === OrgState.ORG_STATE_ACTIVE"
|
||||
[isInactive]="org?.state === OrgState.ORG_STATE_INACTIVE" [hasContributors]="true"
|
||||
stateTooltip="{{'ORG.STATE.'+org?.state | translate}}">
|
||||
<cnsl-contributors topContributors [totalResult]="totalMemberResult" [loading]="loading$ | async"
|
||||
[membersSubject]="membersSubject" title="{{ 'PROJECT.MEMBER.TITLE' | translate }}"
|
||||
description="{{ 'PROJECT.MEMBER.TITLEDESC' | translate }}" (addClicked)="openAddMember()"
|
||||
(showDetailClicked)="showDetail()" (refreshClicked)="loadMembers()" [disabled]="false">
|
||||
<cnsl-top-view
|
||||
[hasBackButton]="false"
|
||||
title="{{ org?.name }}"
|
||||
[isActive]="org?.state === OrgState.ORG_STATE_ACTIVE"
|
||||
[isInactive]="org?.state === OrgState.ORG_STATE_INACTIVE"
|
||||
[hasContributors]="true"
|
||||
stateTooltip="{{ 'ORG.STATE.' + org?.state | translate }}"
|
||||
>
|
||||
<cnsl-contributors
|
||||
topContributors
|
||||
[totalResult]="totalMemberResult"
|
||||
[loading]="loading$ | async"
|
||||
[membersSubject]="membersSubject"
|
||||
title="{{ 'PROJECT.MEMBER.TITLE' | translate }}"
|
||||
description="{{ 'PROJECT.MEMBER.TITLEDESC' | translate }}"
|
||||
(addClicked)="openAddMember()"
|
||||
(showDetailClicked)="showDetail()"
|
||||
(refreshClicked)="loadMembers()"
|
||||
[disabled]="false"
|
||||
>
|
||||
</cnsl-contributors>
|
||||
|
||||
<cnsl-info-row topContent *ngIf="org" [org]="org"></cnsl-info-row>
|
||||
</cnsl-top-view>
|
||||
<div class="max-width-container">
|
||||
<cnsl-meta-layout>
|
||||
<ng-container *ngIf="['features.read'] | hasRole | async">
|
||||
<h2 class="org-subtitle">{{'FEATURES.TITLE' | translate}}</h2>
|
||||
<p class="org-top-desc cnsl-secondary-text">{{'FEATURES.DESCRIPTION' | translate}}</p>
|
||||
<cnsl-zitadel-tier [features]="features"></cnsl-zitadel-tier>
|
||||
</ng-container>
|
||||
|
||||
<ng-template cnslHasRole [hasRole]="['policy.read']">
|
||||
<cnsl-policy-grid class="policies" [type]="PolicyComponentServiceType.MGMT"></cnsl-policy-grid>
|
||||
<cnsl-policy-grid [type]="PolicyComponentServiceType.MGMT"></cnsl-policy-grid>
|
||||
</ng-template>
|
||||
|
||||
<div metainfo>
|
||||
<cnsl-changes *ngIf="org" [changeType]="ChangeType.ORG" [id]="org.id"></cnsl-changes>
|
||||
</div>
|
||||
</cnsl-meta-layout>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -8,8 +8,3 @@
|
||||
text-transform: uppercase;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.policies {
|
||||
display: block;
|
||||
padding-top: 2rem;
|
||||
}
|
||||
|
@@ -1,7 +1,6 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { RoleGuard } from 'src/app/guards/role.guard';
|
||||
import { FeatureServiceType } from 'src/app/modules/features/features.component';
|
||||
import { PolicyComponentServiceType, PolicyComponentType } from 'src/app/modules/policies/policy-component-types.enum';
|
||||
|
||||
import { OrgDetailComponent } from './org-detail/org-detail.component';
|
||||
@@ -30,15 +29,6 @@ const routes: Routes = [
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'features',
|
||||
loadChildren: () => import('src/app/modules/features/features.module').then((m) => m.FeaturesModule),
|
||||
canActivate: [RoleGuard],
|
||||
data: {
|
||||
roles: ['features.read'],
|
||||
serviceType: FeatureServiceType.MGMT,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'policy',
|
||||
children: [
|
||||
|
@@ -14,7 +14,6 @@ import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
|
||||
import { MemberCreateDialogModule } from 'src/app/modules/add-member-dialog/member-create-dialog.module';
|
||||
import { CardModule } from 'src/app/modules/card/card.module';
|
||||
import { ContributorsModule } from 'src/app/modules/contributors/contributors.module';
|
||||
import { FeaturesModule } from 'src/app/modules/features/features.module';
|
||||
import { InfoRowModule } from 'src/app/modules/info-row/info-row.module';
|
||||
import { InfoSectionModule } from 'src/app/modules/info-section/info-section.module';
|
||||
import { InputModule } from 'src/app/modules/input/input.module';
|
||||
@@ -23,11 +22,9 @@ import { PolicyGridModule } from 'src/app/modules/policy-grid/policy-grid.module
|
||||
import { SharedModule } from 'src/app/modules/shared/shared.module';
|
||||
import { TopViewModule } from 'src/app/modules/top-view/top-view.module';
|
||||
import { WarnDialogModule } from 'src/app/modules/warn-dialog/warn-dialog.module';
|
||||
import { HasFeaturePipeModule } from 'src/app/pipes/has-feature-pipe/has-feature-pipe.module';
|
||||
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module';
|
||||
|
||||
import { ChangesModule } from '../../modules/changes/changes.module';
|
||||
import { ZitadelTierModule } from '../../modules/zitadel-tier/zitadel-tier.module';
|
||||
import { OrgDetailComponent } from './org-detail/org-detail.component';
|
||||
import { OrgsRoutingModule } from './orgs-routing.module';
|
||||
|
||||
@@ -53,7 +50,6 @@ import { OrgsRoutingModule } from './orgs-routing.module';
|
||||
MatTooltipModule,
|
||||
WarnDialogModule,
|
||||
MemberCreateDialogModule,
|
||||
HasFeaturePipeModule,
|
||||
MatMenuModule,
|
||||
ChangesModule,
|
||||
MatProgressSpinnerModule,
|
||||
@@ -62,8 +58,6 @@ import { OrgsRoutingModule } from './orgs-routing.module';
|
||||
ContributorsModule,
|
||||
CopyToClipboardModule,
|
||||
PolicyGridModule,
|
||||
FeaturesModule,
|
||||
ZitadelTierModule,
|
||||
],
|
||||
})
|
||||
export class OrgsModule {}
|
||||
|
@@ -1,8 +1,12 @@
|
||||
<cnsl-top-view title="{{user?.human ? user.human?.profile?.displayName :
|
||||
user?.machine?.name}}" sub="{{user?.preferredLoginName}}" [isActive]="user?.state === UserState.USER_STATE_ACTIVE"
|
||||
[isInactive]="user?.state === UserState.USER_STATE_INACTIVE" stateTooltip="{{'USER.STATE.'+user?.state | translate}}"
|
||||
[hasBackButton]="(['org.read'] | hasRole | async)" (backRouterLink)="['/']">
|
||||
|
||||
<cnsl-top-view
|
||||
title="{{ user?.human ? user.human?.profile?.displayName : user?.machine?.name }}"
|
||||
sub="{{ user?.preferredLoginName }}"
|
||||
[isActive]="user?.state === UserState.USER_STATE_ACTIVE"
|
||||
[isInactive]="user?.state === UserState.USER_STATE_INACTIVE"
|
||||
stateTooltip="{{ 'USER.STATE.' + user?.state | translate }}"
|
||||
[hasBackButton]="['org.read'] | hasRole | async"
|
||||
(backRouterLink)="(['/'])"
|
||||
>
|
||||
<span *ngIf="!loading && !user">{{ 'USER.PAGES.NOUSER' | translate }}</span>
|
||||
<cnsl-info-row topContent *ngIf="user" [user]="user"></cnsl-info-row>
|
||||
</cnsl-top-view>
|
||||
@@ -15,35 +19,62 @@
|
||||
<cnsl-meta-layout>
|
||||
<cnsl-sidenav [(ngModel)]="currentSetting" [settingsList]="settingsList">
|
||||
<ng-container *ngIf="currentSetting === 'general'">
|
||||
<cnsl-card *ngIf="user && user.human && user.human.profile" class=" app-card"
|
||||
title="{{ 'USER.PROFILE.TITLE' | translate }}">
|
||||
<cnsl-detail-form [showEditImage]="true" [preferredLoginName]="user.preferredLoginName" [genders]="genders"
|
||||
[languages]="languages" [username]="user.userName" [user]="user.human" [disabled]="false"
|
||||
(changedLanguage)="changedLanguage($event)" (changeUsernameClicked)="changeUsername()"
|
||||
(submitData)="saveProfile($event)">
|
||||
<cnsl-card
|
||||
*ngIf="user && user.human && user.human.profile"
|
||||
class="app-card"
|
||||
title="{{ 'USER.PROFILE.TITLE' | translate }}"
|
||||
>
|
||||
<cnsl-detail-form
|
||||
[showEditImage]="true"
|
||||
[preferredLoginName]="user.preferredLoginName"
|
||||
[genders]="genders"
|
||||
[languages]="languages"
|
||||
[username]="user.userName"
|
||||
[user]="user.human"
|
||||
[disabled]="false"
|
||||
(changedLanguage)="changedLanguage($event)"
|
||||
(changeUsernameClicked)="changeUsername()"
|
||||
(submitData)="saveProfile($event)"
|
||||
>
|
||||
</cnsl-detail-form>
|
||||
</cnsl-card>
|
||||
|
||||
<cnsl-card *ngIf="user" title="{{ 'USER.LOGINMETHODS.TITLE' | translate }}"
|
||||
description="{{ 'USER.LOGINMETHODS.DESCRIPTION' | translate }}">
|
||||
<button class="icon-button" card-actions mat-icon-button (click)="refreshUser()"
|
||||
matTooltip="{{'ACTIONS.REFRESH' | translate}}">
|
||||
<cnsl-card
|
||||
*ngIf="user"
|
||||
title="{{ 'USER.LOGINMETHODS.TITLE' | translate }}"
|
||||
description="{{ 'USER.LOGINMETHODS.DESCRIPTION' | translate }}"
|
||||
>
|
||||
<button
|
||||
class="icon-button"
|
||||
card-actions
|
||||
mat-icon-button
|
||||
(click)="refreshUser()"
|
||||
matTooltip="{{ 'ACTIONS.REFRESH' | translate }}"
|
||||
>
|
||||
<mat-icon class="icon">refresh</mat-icon>
|
||||
</button>
|
||||
<cnsl-contact *ngIf="user.human" [human]="user.human" [state]="user.state" [canWrite]="true"
|
||||
(editType)="openEditDialog($event)" (enteredPhoneCode)="enteredPhoneCode($event)"
|
||||
(deletedPhone)="deletePhone()" (resendEmailVerification)="resendEmailVerification()"
|
||||
(resendPhoneVerification)="resendPhoneVerification()">
|
||||
<cnsl-contact
|
||||
*ngIf="user.human"
|
||||
[human]="user.human"
|
||||
[state]="user.state"
|
||||
[canWrite]="true"
|
||||
(editType)="openEditDialog($event)"
|
||||
(enteredPhoneCode)="enteredPhoneCode($event)"
|
||||
(deletedPhone)="deletePhone()"
|
||||
(resendEmailVerification)="resendEmailVerification()"
|
||||
(resendPhoneVerification)="resendPhoneVerification()"
|
||||
>
|
||||
</cnsl-contact>
|
||||
</cnsl-card>
|
||||
|
||||
<ng-template cnslHasRole [hasRole]="['user.self.delete']">
|
||||
<cnsl-card title="{{'USER.PAGES.DELETEACCOUNT'| translate}}" [warn]="true">
|
||||
<p>{{'USER.PAGES.DELETEACCOUNT_DESC'| translate}}</p>
|
||||
<cnsl-card title="{{ 'USER.PAGES.DELETEACCOUNT' | translate }}" [warn]="true">
|
||||
<p>{{ 'USER.PAGES.DELETEACCOUNT_DESC' | translate }}</p>
|
||||
|
||||
<div class="delete-account-wrapper">
|
||||
<button color="warn" mat-raised-button (click)="deleteAccount()">{{'USER.PAGES.DELETEACCOUNT_BTN' |
|
||||
translate}}</button>
|
||||
<button color="warn" mat-raised-button (click)="deleteAccount()">
|
||||
{{ 'USER.PAGES.DELETEACCOUNT_BTN' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</cnsl-card>
|
||||
</ng-template>
|
||||
@@ -62,33 +93,48 @@
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="currentSetting === 'grants'">
|
||||
<cnsl-card *ngIf="user?.id" title="{{ 'GRANTS.USER.TITLE' | translate }}"
|
||||
description="{{'GRANTS.USER.DESCRIPTION' | translate }}">
|
||||
<cnsl-user-grants [userId]="user.id" [context]="USERGRANTCONTEXT"
|
||||
[displayedColumns]="['select', 'org','projectId', 'type','creationDate','changeDate', 'roleNamesList', 'actions']"
|
||||
[disableWrite]="((['user.grant.write$'] | hasRole) | async) === false"
|
||||
[disableDelete]="((['user.grant.delete$'] | hasRole) | async) === false">
|
||||
<cnsl-card
|
||||
*ngIf="user?.id"
|
||||
title="{{ 'GRANTS.USER.TITLE' | translate }}"
|
||||
description="{{ 'GRANTS.USER.DESCRIPTION' | translate }}"
|
||||
>
|
||||
<cnsl-user-grants
|
||||
[userId]="user.id"
|
||||
[context]="USERGRANTCONTEXT"
|
||||
[displayedColumns]="[
|
||||
'select',
|
||||
'org',
|
||||
'projectId',
|
||||
'type',
|
||||
'creationDate',
|
||||
'changeDate',
|
||||
'roleNamesList',
|
||||
'actions'
|
||||
]"
|
||||
[disableWrite]="(['user.grant.write$'] | hasRole | async) === false"
|
||||
[disableDelete]="(['user.grant.delete$'] | hasRole | async) === false"
|
||||
>
|
||||
</cnsl-user-grants>
|
||||
</cnsl-card>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="currentSetting === 'memberships'">
|
||||
<cnsl-card *ngIf="user?.id" title="{{ 'USER.MEMBERSHIPS.TITLE' | translate }}"
|
||||
description="{{'USER.MEMBERSHIPS.DESCRIPTION' | translate }}">
|
||||
<cnsl-card
|
||||
*ngIf="user?.id"
|
||||
title="{{ 'USER.MEMBERSHIPS.TITLE' | translate }}"
|
||||
description="{{ 'USER.MEMBERSHIPS.DESCRIPTION' | translate }}"
|
||||
>
|
||||
<cnsl-memberships-table></cnsl-memberships-table>
|
||||
</cnsl-card>
|
||||
</ng-container>
|
||||
|
||||
<ng-template cnslHasFeature [hasFeature]="['metadata.user']">
|
||||
<ng-container *ngIf="currentSetting === 'metadata'">
|
||||
<cnsl-metadata *ngIf="user?.id" [userId]="user.id"></cnsl-metadata>
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
<ng-container *ngIf="currentSetting === 'metadata'">
|
||||
<cnsl-metadata *ngIf="user?.id" [userId]="user.id"></cnsl-metadata>
|
||||
</ng-container>
|
||||
</cnsl-sidenav>
|
||||
|
||||
<div metainfo>
|
||||
<cnsl-changes class="changes" [refresh]="refreshChanges$" [changeType]="ChangeType.MYUSER">
|
||||
</cnsl-changes>
|
||||
<cnsl-changes class="changes" [refresh]="refreshChanges$" [changeType]="ChangeType.MYUSER"> </cnsl-changes>
|
||||
</div>
|
||||
</cnsl-meta-layout>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -1,79 +1,79 @@
|
||||
<div class="method-col">
|
||||
<div class="method-row">
|
||||
<div class="left">
|
||||
<span class="label cnsl-secondary-text">{{ 'USER.PROFILE.PASSWORD' | translate }}</span>
|
||||
<span class="name">*********</span>
|
||||
<div class="contact-method-col">
|
||||
<div class="contact-method-row">
|
||||
<div class="left">
|
||||
<span class="label cnsl-secondary-text">{{ 'USER.PROFILE.PASSWORD' | translate }}</span>
|
||||
<span class="name">*********</span>
|
||||
|
||||
<ng-content select="[pwdAction]"></ng-content>
|
||||
<ng-content select="[pwdAction]"></ng-content>
|
||||
</div>
|
||||
|
||||
<div class="right">
|
||||
<a matTooltip="{{'USER.PASSWORD.SET' | translate}}" [disabled]="!canWrite" [routerLink]="['password']"
|
||||
mat-icon-button>
|
||||
<i class="las la-pen"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="right">
|
||||
<a matTooltip="{{'USER.PASSWORD.SET' | translate}}" [disabled]="!canWrite" [routerLink]="['password']"
|
||||
mat-icon-button>
|
||||
<i class="las la-pen"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="contact-method-row">
|
||||
<div class="left">
|
||||
<span class="label cnsl-secondary-text">{{ 'USER.EMAIL' | translate }}</span>
|
||||
<span class="name">{{human?.email?.email}}</span>
|
||||
<span *ngIf="human?.email?.isEmailVerified" class="contact-state verified">{{'USER.EMAILVERIFIED' |
|
||||
translate}}</span>
|
||||
<div *ngIf="!human?.email?.isEmailVerified" class="block">
|
||||
<span class="contact-state notverified">{{'USER.NOTVERIFIED' | translate}}</span>
|
||||
|
||||
<div class="method-row">
|
||||
<div class="left">
|
||||
<span class="label cnsl-secondary-text">{{ 'USER.EMAIL' | translate }}</span>
|
||||
<span class="name">{{human?.email?.email}}</span>
|
||||
<span *ngIf="human?.email?.isEmailVerified" class="contact-state verified">{{'USER.EMAILVERIFIED' |
|
||||
translate}}</span>
|
||||
<div *ngIf="!human?.email?.isEmailVerified" class="block">
|
||||
<span class="contact-state notverified">{{'USER.NOTVERIFIED' | translate}}</span>
|
||||
<ng-container *ngIf="human?.email">
|
||||
<a *ngIf="canWrite" class="verify cnsl-secondary-text"
|
||||
matTooltip="{{'USER.LOGINMETHODS.EMAIL.RESEND' | translate}}"
|
||||
(click)="emitEmailVerification()">{{'USER.LOGINMETHODS.RESENDCODE' | translate}}</a>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="human?.email">
|
||||
<a *ngIf="canWrite" class="verify cnsl-secondary-text"
|
||||
matTooltip="{{'USER.LOGINMETHODS.EMAIL.RESEND' | translate}}"
|
||||
(click)="emitEmailVerification()">{{'USER.LOGINMETHODS.RESENDCODE' | translate}}</a>
|
||||
</ng-container>
|
||||
</div>
|
||||
<ng-content select="[emailAction]"></ng-content>
|
||||
</div>
|
||||
|
||||
<ng-content select="[emailAction]"></ng-content>
|
||||
<div class="right">
|
||||
<button matTooltip="{{'ACTIONS.EDIT' | translate}}"
|
||||
[disabled]="!canWrite || state === UserState.USER_STATE_INITIAL"
|
||||
(click)="openEditDialog(EditDialogType.EMAIL)" mat-icon-button>
|
||||
<i class="las la-pen"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="right">
|
||||
<button matTooltip="{{'ACTIONS.EDIT' | translate}}"
|
||||
[disabled]="!canWrite || state === UserState.USER_STATE_INITIAL" (click)="openEditDialog(EditDialogType.EMAIL)"
|
||||
mat-icon-button>
|
||||
<i class="las la-pen"></i>
|
||||
</button>
|
||||
<div class="contact-method-row">
|
||||
<div class="left">
|
||||
<span class="label cnsl-secondary-text">{{ 'USER.PHONE' | translate }}</span>
|
||||
<span class="name">{{human?.phone?.phone ? human.phone?.phone : ('USER.PHONEEMPTY' | translate)}}</span>
|
||||
<span *ngIf="human?.phone?.isPhoneVerified" class="contact-state verified">{{'USER.PHONEVERIFIED' |
|
||||
translate}}</span>
|
||||
<div *ngIf="human.phone?.phone && !human?.phone?.isPhoneVerified" class="block">
|
||||
<span class="contact-state notverified">{{'USER.NOTVERIFIED' | translate}}</span>
|
||||
|
||||
<ng-container *ngIf="human?.phone?.phone">
|
||||
<a *ngIf="!disablePhoneCode && canWrite" class="verify cnsl-secondary-text"
|
||||
matTooltip="{{'USER.LOGINMETHODS.ENTERCODE_DESC' | translate}}"
|
||||
(click)="enterCode()">{{'USER.LOGINMETHODS.ENTERCODE' | translate}}</a>
|
||||
<a *ngIf="canWrite" class="verify cnsl-secondary-text"
|
||||
matTooltip="{{'USER.LOGINMETHODS.PHONE.RESEND' | translate}}"
|
||||
(click)="emitPhoneVerification()">{{'USER.LOGINMETHODS.RESENDCODE' | translate}}</a>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
<ng-content select="[phoneAction]"></ng-content>
|
||||
</div>
|
||||
|
||||
<div class="right">
|
||||
<button matTooltip="{{'ACTIONS.DELETE' | translate}}" *ngIf="human && human.phone?.phone" color="warn"
|
||||
(click)="emitDeletePhone()" mat-icon-button>
|
||||
<i class="las la-trash"></i>
|
||||
</button>
|
||||
<button matTooltip="{{'ACTIONS.EDIT' | translate}}" [disabled]="!canWrite"
|
||||
(click)="openEditDialog(EditDialogType.PHONE)" mat-icon-button>
|
||||
<i class="las la-pen"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="method-row">
|
||||
<div class="left">
|
||||
<span class="label cnsl-secondary-text">{{ 'USER.PHONE' | translate }}</span>
|
||||
<span class="name">{{human?.phone?.phone ? human.phone?.phone : ('USER.PHONEEMPTY' | translate)}}</span>
|
||||
<span *ngIf="human?.phone?.isPhoneVerified" class="contact-state verified">{{'USER.PHONEVERIFIED' |
|
||||
translate}}</span>
|
||||
<div *ngIf="human.phone?.phone && !human?.phone?.isPhoneVerified" class="block">
|
||||
<span class="contact-state notverified">{{'USER.NOTVERIFIED' | translate}}</span>
|
||||
|
||||
<ng-container *ngIf="human?.phone?.phone">
|
||||
<a *ngIf="!disablePhoneCode && canWrite" class="verify cnsl-secondary-text"
|
||||
matTooltip="{{'USER.LOGINMETHODS.ENTERCODE_DESC' | translate}}"
|
||||
(click)="enterCode()">{{'USER.LOGINMETHODS.ENTERCODE' | translate}}</a>
|
||||
<a *ngIf="canWrite" class="verify cnsl-secondary-text"
|
||||
matTooltip="{{'USER.LOGINMETHODS.PHONE.RESEND' | translate}}"
|
||||
(click)="emitPhoneVerification()">{{'USER.LOGINMETHODS.RESENDCODE' | translate}}</a>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
<ng-content select="[phoneAction]"></ng-content>
|
||||
</div>
|
||||
|
||||
<div class="right">
|
||||
<button matTooltip="{{'ACTIONS.DELETE' | translate}}" *ngIf="human && human.phone?.phone" color="warn"
|
||||
(click)="emitDeletePhone()" mat-icon-button>
|
||||
<i class="las la-trash"></i>
|
||||
</button>
|
||||
<button matTooltip="{{'ACTIONS.EDIT' | translate}}" [disabled]="!canWrite"
|
||||
(click)="openEditDialog(EditDialogType.PHONE)" mat-icon-button>
|
||||
<i class="las la-pen"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@@ -1,74 +1,84 @@
|
||||
.method-col {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: -0.5rem;
|
||||
@use '@angular/material' as mat;
|
||||
|
||||
.method-row {
|
||||
@mixin contact-theme($theme) {
|
||||
$foreground: map-get($theme, foreground);
|
||||
$background: map-get($theme, background);
|
||||
$is-dark-theme: map-get($theme, is-dark);
|
||||
$warn: map-get($theme, warn);
|
||||
$warn-color: map-get($warn, default);
|
||||
|
||||
.contact-method-col {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0.5rem;
|
||||
padding-right: 0;
|
||||
border-bottom: 1px solid #ffffff20;
|
||||
flex-wrap: wrap;
|
||||
flex-direction: column;
|
||||
margin: -0.5rem;
|
||||
|
||||
.left {
|
||||
.label {
|
||||
font-size: 13px;
|
||||
margin-bottom: 0.5rem;
|
||||
min-width: 100px;
|
||||
display: block;
|
||||
}
|
||||
.contact-method-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0.5rem;
|
||||
padding-right: 0;
|
||||
border-bottom: 1px solid #ffffff20;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.name {
|
||||
display: block;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.contact-state {
|
||||
font-size: 14px;
|
||||
margin-bottom: 0.5rem;
|
||||
|
||||
&.verified {
|
||||
color: #85d996;
|
||||
.left {
|
||||
.label {
|
||||
font-size: 13px;
|
||||
margin-bottom: 0.5rem;
|
||||
min-width: 100px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
&.notverified {
|
||||
color: var(--warn);
|
||||
margin-right: 1rem;
|
||||
.name {
|
||||
display: block;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.contact-state {
|
||||
font-size: 14px;
|
||||
margin-bottom: 0.5rem;
|
||||
|
||||
&.verified {
|
||||
color: #85d996;
|
||||
display: block;
|
||||
}
|
||||
|
||||
&.notverified {
|
||||
color: var(--warn);
|
||||
margin-right: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.block {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.block {
|
||||
display: block;
|
||||
.right {
|
||||
flex-basis: 70px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
.right {
|
||||
flex-basis: 70px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.verified-icon {
|
||||
font-size: 1.2rem;
|
||||
line-height: 1.2rem;
|
||||
height: 1.2rem;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.verified-icon {
|
||||
font-size: 1.2rem;
|
||||
line-height: 1.2rem;
|
||||
height: 1.2rem;
|
||||
cursor: default;
|
||||
}
|
||||
.verify {
|
||||
text-decoration: none;
|
||||
font-size: 0.8rem;
|
||||
border-radius: 0.5rem;
|
||||
cursor: pointer;
|
||||
word-wrap: none;
|
||||
white-space: nowrap;
|
||||
margin-right: 1rem;
|
||||
|
||||
.verify {
|
||||
text-decoration: none;
|
||||
font-size: 0.8rem;
|
||||
border-radius: 0.5rem;
|
||||
cursor: pointer;
|
||||
word-wrap: none;
|
||||
white-space: nowrap;
|
||||
margin-right: 1rem;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
&:hover {
|
||||
color: map-get($foreground, text);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -38,12 +38,11 @@ import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.mod
|
||||
import { LocalizedDatePipeModule } from 'src/app/pipes/localized-date-pipe/localized-date-pipe.module';
|
||||
import { TimestampToDatePipeModule } from 'src/app/pipes/timestamp-to-date-pipe/timestamp-to-date-pipe.module';
|
||||
|
||||
import { HasFeatureModule } from '../../../directives/has-feature/has-feature.module';
|
||||
import { InfoRowModule } from '../../../modules/info-row/info-row.module';
|
||||
import { AuthFactorDialogComponent } from './auth-user-detail/auth-factor-dialog/auth-factor-dialog.component';
|
||||
import { AuthPasswordlessComponent } from './auth-user-detail/auth-passwordless/auth-passwordless.component';
|
||||
import {
|
||||
DialogPasswordlessComponent,
|
||||
DialogPasswordlessComponent,
|
||||
} from './auth-user-detail/auth-passwordless/dialog-passwordless/dialog-passwordless.component';
|
||||
import { AuthUserDetailComponent } from './auth-user-detail/auth-user-detail.component';
|
||||
import { AuthUserMfaComponent } from './auth-user-detail/auth-user-mfa/auth-user-mfa.component';
|
||||
@@ -106,7 +105,6 @@ import { UserMfaComponent } from './user-detail/user-mfa/user-mfa.component';
|
||||
MatIconModule,
|
||||
CardModule,
|
||||
MatProgressSpinnerModule,
|
||||
HasFeatureModule,
|
||||
MatTooltipModule,
|
||||
HasRoleModule,
|
||||
TranslateModule,
|
||||
|
@@ -1,20 +1,36 @@
|
||||
<cnsl-top-view *ngIf="user" title="{{user?.human ? user.human?.profile?.displayName :
|
||||
user?.machine?.name}}" docLink="https://docs.zitadel.ch/docs/guides/basics/projects"
|
||||
sub="{{user?.preferredLoginName}}" [isActive]="user?.state === UserState.USER_STATE_ACTIVE"
|
||||
[isInactive]="user?.state === UserState.USER_STATE_INACTIVE" stateTooltip="{{'USER.STATE.'+user?.state | translate}}"
|
||||
(backClicked)="navigateBack()" [hasActions]="['user.write$', 'user.write:'+user?.id] | hasRole | async">
|
||||
<ng-template topActions cnslHasRole [hasRole]="['user.write$', 'user.write:'+user?.id]">
|
||||
<button mat-menu-item color="warn" *ngIf="user?.state === UserState.USER_STATE_LOCKED"
|
||||
(click)="unlockUser()">{{'USER.PAGES.UNLOCK' |
|
||||
translate}}</button>
|
||||
<button mat-menu-item *ngIf="user?.state === UserState.USER_STATE_ACTIVE"
|
||||
(click)="changeState(UserState.USER_STATE_INACTIVE)">{{'USER.PAGES.DEACTIVATE' |
|
||||
translate}}</button>
|
||||
<button mat-menu-item *ngIf="user?.state === UserState.USER_STATE_INACTIVE"
|
||||
(click)="changeState(UserState.USER_STATE_ACTIVE)">{{'USER.PAGES.REACTIVATE' | translate}}</button>
|
||||
<ng-template cnslHasRole [hasRole]="['user.delete$', 'user.delete:'+user?.id]">
|
||||
<button mat-menu-item matTooltip="{{'USER.PAGES.DELETE' | translate}}" (click)="deleteUser()"><span
|
||||
[style.color]="'var(--warn)'">{{'USER.PAGES.DELETE' | translate}}</span></button>
|
||||
<cnsl-top-view
|
||||
*ngIf="user"
|
||||
title="{{ user?.human ? user.human?.profile?.displayName : user?.machine?.name }}"
|
||||
docLink="https://docs.zitadel.ch/docs/guides/basics/projects"
|
||||
sub="{{ user?.preferredLoginName }}"
|
||||
[isActive]="user?.state === UserState.USER_STATE_ACTIVE"
|
||||
[isInactive]="user?.state === UserState.USER_STATE_INACTIVE"
|
||||
stateTooltip="{{ 'USER.STATE.' + user?.state | translate }}"
|
||||
(backClicked)="navigateBack()"
|
||||
[hasActions]="['user.write$', 'user.write:' + user?.id] | hasRole | async"
|
||||
>
|
||||
<ng-template topActions cnslHasRole [hasRole]="['user.write$', 'user.write:' + user?.id]">
|
||||
<button mat-menu-item color="warn" *ngIf="user?.state === UserState.USER_STATE_LOCKED" (click)="unlockUser()">
|
||||
{{ 'USER.PAGES.UNLOCK' | translate }}
|
||||
</button>
|
||||
<button
|
||||
mat-menu-item
|
||||
*ngIf="user?.state === UserState.USER_STATE_ACTIVE"
|
||||
(click)="changeState(UserState.USER_STATE_INACTIVE)"
|
||||
>
|
||||
{{ 'USER.PAGES.DEACTIVATE' | translate }}
|
||||
</button>
|
||||
<button
|
||||
mat-menu-item
|
||||
*ngIf="user?.state === UserState.USER_STATE_INACTIVE"
|
||||
(click)="changeState(UserState.USER_STATE_ACTIVE)"
|
||||
>
|
||||
{{ 'USER.PAGES.REACTIVATE' | translate }}
|
||||
</button>
|
||||
<ng-template cnslHasRole [hasRole]="['user.delete$', 'user.delete:' + user?.id]">
|
||||
<button mat-menu-item matTooltip="{{ 'USER.PAGES.DELETE' | translate }}" (click)="deleteUser()">
|
||||
<span [style.color]="'var(--warn)'">{{ 'USER.PAGES.DELETE' | translate }}</span>
|
||||
</button>
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
<cnsl-info-row topContent *ngIf="user" [user]="user"></cnsl-info-row>
|
||||
@@ -26,48 +42,83 @@
|
||||
|
||||
<p class="no-user-error" *ngIf="!loading && !user">{{ 'USER.PAGES.NOUSER' | translate }}</p>
|
||||
|
||||
<div class="max-width-container" *ngIf="user && (['user.write$','user.write:' + user.id] | hasRole) as canWrite$">
|
||||
<div class="max-width-container" *ngIf="user && (['user.write$', 'user.write:' + user.id] | hasRole) as canWrite$">
|
||||
<cnsl-meta-layout>
|
||||
<cnsl-sidenav [(ngModel)]="currentSetting" [settingsList]="settingsList">
|
||||
<div *ngIf="error" class="max-width-container">
|
||||
<p>{{error}}</p>
|
||||
<p>{{ error }}</p>
|
||||
</div>
|
||||
|
||||
<div class="max-width-container">
|
||||
<cnsl-info-section class="locked" *ngIf="user?.state === UserState.USER_STATE_LOCKED"
|
||||
[type]="InfoSectionType.WARN">
|
||||
{{'USER.PAGES.LOCKEDDESCRIPTION' | translate}}</cnsl-info-section>
|
||||
<cnsl-info-section class="locked" *ngIf="user?.state === UserState.USER_STATE_LOCKED" [type]="InfoSectionType.WARN">
|
||||
{{ 'USER.PAGES.LOCKEDDESCRIPTION' | translate }}</cnsl-info-section
|
||||
>
|
||||
<span *ngIf="!loading && !user">{{ 'USER.PAGES.NOUSER' | translate }}</span>
|
||||
|
||||
<ng-container *ngIf="currentSetting === 'general'">
|
||||
<ng-template cnslHasRole [hasRole]="['user.read$', 'user.read:'+user?.id]">
|
||||
<ng-template cnslHasRole [hasRole]="['user.read$', 'user.read:' + user?.id]">
|
||||
<cnsl-card *ngIf="user.human" title="{{ 'USER.PROFILE.TITLE' | translate }}">
|
||||
<cnsl-detail-form [preferredLoginName]="user.preferredLoginName"
|
||||
[disabled]="(canWrite$ | async) === false" [genders]="genders" [languages]="languages"
|
||||
[username]="user.userName" [user]="user.human" (submitData)="saveProfile($event)"
|
||||
(changeUsernameClicked)="changeUsername()">
|
||||
<cnsl-detail-form
|
||||
[preferredLoginName]="user.preferredLoginName"
|
||||
[disabled]="(canWrite$ | async) === false"
|
||||
[genders]="genders"
|
||||
[languages]="languages"
|
||||
[username]="user.userName"
|
||||
[user]="user.human"
|
||||
(submitData)="saveProfile($event)"
|
||||
(changeUsernameClicked)="changeUsername()"
|
||||
>
|
||||
</cnsl-detail-form>
|
||||
</cnsl-card>
|
||||
|
||||
<cnsl-card *ngIf="user.human" title="{{ 'USER.LOGINMETHODS.TITLE' | translate }}"
|
||||
description="{{ 'USER.LOGINMETHODS.DESCRIPTION' | translate }}">
|
||||
<button card-actions class="icon-button" mat-icon-button (click)="refreshUser()"
|
||||
matTooltip="{{'ACTIONS.REFRESH' | translate}}">
|
||||
<cnsl-card
|
||||
*ngIf="user.human"
|
||||
title="{{ 'USER.LOGINMETHODS.TITLE' | translate }}"
|
||||
description="{{ 'USER.LOGINMETHODS.DESCRIPTION' | translate }}"
|
||||
>
|
||||
<button
|
||||
card-actions
|
||||
class="icon-button"
|
||||
mat-icon-button
|
||||
(click)="refreshUser()"
|
||||
matTooltip="{{ 'ACTIONS.REFRESH' | translate }}"
|
||||
>
|
||||
<mat-icon class="icon">refresh</mat-icon>
|
||||
</button>
|
||||
<cnsl-contact [disablePhoneCode]="true" [state]="user.state"
|
||||
[canWrite]="(['user.write:' + user?.id, 'user.write$'] | hasRole | async)" *ngIf="user?.human"
|
||||
[human]="user.human" (editType)="openEditDialog($event)" (deletedPhone)="deletePhone()"
|
||||
<cnsl-contact
|
||||
[disablePhoneCode]="true"
|
||||
[state]="user.state"
|
||||
[canWrite]="['user.write:' + user?.id, 'user.write$'] | hasRole | async"
|
||||
*ngIf="user?.human"
|
||||
[human]="user.human"
|
||||
(editType)="openEditDialog($event)"
|
||||
(deletedPhone)="deletePhone()"
|
||||
(resendEmailVerification)="resendEmailVerification()"
|
||||
(resendPhoneVerification)="resendPhoneVerification()">
|
||||
<button pwdAction [disabled]="(canWrite$ | async) === false" (click)="sendSetPasswordNotification()"
|
||||
mat-stroked-button color="primary"
|
||||
*ngIf="user.state !== UserState.USER_STATE_LOCKED && user.state !== UserState.USER_STATE_INACTIVE && user.state !== UserState.USER_STATE_INITIAL">{{
|
||||
'USER.PASSWORD.RESENDNOTIFICATION' | translate }}</button>
|
||||
<button emailAction [disabled]="(canWrite$ | async) === false" class="resendemail"
|
||||
*ngIf="user.state === UserState.USER_STATE_INITIAL" mat-stroked-button color="primary"
|
||||
(click)="resendInitEmail()">{{'USER.RESENDINITIALEMAIL' |
|
||||
translate}}</button>
|
||||
(resendPhoneVerification)="resendPhoneVerification()"
|
||||
>
|
||||
<button
|
||||
pwdAction
|
||||
[disabled]="(canWrite$ | async) === false"
|
||||
(click)="sendSetPasswordNotification()"
|
||||
mat-stroked-button
|
||||
*ngIf="
|
||||
user.state !== UserState.USER_STATE_LOCKED &&
|
||||
user.state !== UserState.USER_STATE_INACTIVE &&
|
||||
user.state !== UserState.USER_STATE_INITIAL
|
||||
"
|
||||
>
|
||||
{{ 'USER.PASSWORD.RESENDNOTIFICATION' | translate }}
|
||||
</button>
|
||||
<button
|
||||
emailAction
|
||||
[disabled]="(canWrite$ | async) === false"
|
||||
class="resendemail"
|
||||
*ngIf="user.state === UserState.USER_STATE_INITIAL"
|
||||
mat-stroked-button
|
||||
(click)="resendInitEmail()"
|
||||
>
|
||||
{{ 'USER.RESENDINITIALEMAIL' | translate }}
|
||||
</button>
|
||||
</cnsl-contact>
|
||||
</cnsl-card>
|
||||
</ng-template>
|
||||
@@ -80,25 +131,35 @@
|
||||
|
||||
<ng-container *ngIf="currentSetting && currentSetting === 'general'">
|
||||
<cnsl-card *ngIf="user.machine" title="{{ 'USER.MACHINE.TITLE' | translate }}">
|
||||
<cnsl-detail-form-machine [disabled]="(canWrite$ | async) === false" [username]="user.userName"
|
||||
[user]="user.machine" (submitData)="saveMachine($event)">
|
||||
<cnsl-detail-form-machine
|
||||
[disabled]="(canWrite$ | async) === false"
|
||||
[username]="user.userName"
|
||||
[user]="user.machine"
|
||||
(submitData)="saveMachine($event)"
|
||||
>
|
||||
</cnsl-detail-form-machine>
|
||||
</cnsl-card>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="currentSetting && currentSetting === 'pat'">
|
||||
<ng-template cnslHasRole [hasRole]="['user.read$', 'user.read:'+user?.id]">
|
||||
<cnsl-card *ngIf="user.machine && user.id" title="{{ 'USER.MACHINE.TOKENSTITLE' | translate }}"
|
||||
description="{{ 'USER.MACHINE.TOKENSDESC' | translate }}">
|
||||
<ng-template cnslHasRole [hasRole]="['user.read$', 'user.read:' + user?.id]">
|
||||
<cnsl-card
|
||||
*ngIf="user.machine && user.id"
|
||||
title="{{ 'USER.MACHINE.TOKENSTITLE' | translate }}"
|
||||
description="{{ 'USER.MACHINE.TOKENSDESC' | translate }}"
|
||||
>
|
||||
<cnsl-personal-access-tokens [userId]="user.id"></cnsl-personal-access-tokens>
|
||||
</cnsl-card>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="currentSetting && currentSetting === 'keys'">
|
||||
<ng-template cnslHasRole [hasRole]="['user.read$', 'user.read:'+user?.id]">
|
||||
<cnsl-card *ngIf="user.machine && user.id" title="{{ 'USER.MACHINE.KEYSTITLE' | translate }}"
|
||||
description="{{ 'USER.MACHINE.KEYSDESC' | translate }}">
|
||||
<ng-template cnslHasRole [hasRole]="['user.read$', 'user.read:' + user?.id]">
|
||||
<cnsl-card
|
||||
*ngIf="user.machine && user.id"
|
||||
title="{{ 'USER.MACHINE.KEYSTITLE' | translate }}"
|
||||
description="{{ 'USER.MACHINE.KEYSDESC' | translate }}"
|
||||
>
|
||||
<cnsl-machine-keys [userId]="user.id"></cnsl-machine-keys>
|
||||
</cnsl-card>
|
||||
</ng-template>
|
||||
@@ -114,34 +175,40 @@
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="currentSetting && currentSetting === 'grants'">
|
||||
<cnsl-card *ngIf="user?.id" title="{{ 'GRANTS.USER.TITLE' | translate }}"
|
||||
description="{{'GRANTS.USER.DESCRIPTION' | translate }}">
|
||||
<cnsl-user-grants [userId]="user.id" [context]="USERGRANTCONTEXT"
|
||||
[displayedColumns]="['select', 'projectId', 'creationDate','changeDate', 'roleNamesList', 'actions']"
|
||||
[disableWrite]="((['user.grant.write$'] | hasRole) | async) === false"
|
||||
[disableDelete]="((['user.grant.delete$'] | hasRole) | async) === false">
|
||||
<cnsl-card
|
||||
*ngIf="user?.id"
|
||||
title="{{ 'GRANTS.USER.TITLE' | translate }}"
|
||||
description="{{ 'GRANTS.USER.DESCRIPTION' | translate }}"
|
||||
>
|
||||
<cnsl-user-grants
|
||||
[userId]="user.id"
|
||||
[context]="USERGRANTCONTEXT"
|
||||
[displayedColumns]="['select', 'projectId', 'creationDate', 'changeDate', 'roleNamesList', 'actions']"
|
||||
[disableWrite]="(['user.grant.write$'] | hasRole | async) === false"
|
||||
[disableDelete]="(['user.grant.delete$'] | hasRole | async) === false"
|
||||
>
|
||||
</cnsl-user-grants>
|
||||
</cnsl-card>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="currentSetting && currentSetting === 'memberships'">
|
||||
<cnsl-card *ngIf="user?.id" title="{{ 'USER.MEMBERSHIPS.TITLE' | translate }}"
|
||||
description="{{'USER.MEMBERSHIPS.DESCRIPTION' | translate }}">
|
||||
<cnsl-card
|
||||
*ngIf="user?.id"
|
||||
title="{{ 'USER.MEMBERSHIPS.TITLE' | translate }}"
|
||||
description="{{ 'USER.MEMBERSHIPS.DESCRIPTION' | translate }}"
|
||||
>
|
||||
<cnsl-memberships-table [userId]="user.id"></cnsl-memberships-table>
|
||||
</cnsl-card>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="currentSetting && currentSetting === 'metadata'">
|
||||
<ng-template cnslHasFeature [hasFeature]="['metadata.user']">
|
||||
<cnsl-metadata *ngIf="user" [userId]="user.id"></cnsl-metadata>
|
||||
</ng-template>
|
||||
<cnsl-metadata *ngIf="user" [userId]="user.id"></cnsl-metadata>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
</cnsl-sidenav>
|
||||
<div metainfo>
|
||||
<cnsl-changes class="changes" [refresh]="refreshChanges$" [changeType]="ChangeType.USER" [id]="user.id">
|
||||
</cnsl-changes>
|
||||
</div>
|
||||
</cnsl-meta-layout>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -1,18 +0,0 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
|
||||
import { HasFeaturePipe } from './has-feature.pipe';
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
HasFeaturePipe,
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
],
|
||||
exports: [
|
||||
HasFeaturePipe,
|
||||
],
|
||||
})
|
||||
export class HasFeaturePipeModule { }
|
@@ -1,14 +0,0 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
|
||||
|
||||
@Pipe({
|
||||
name: 'hasFeature',
|
||||
})
|
||||
export class HasFeaturePipe implements PipeTransform {
|
||||
constructor(private authService: GrpcAuthService) { }
|
||||
|
||||
public transform(values: string[]): Observable<boolean> {
|
||||
return this.authService.canUseFeature(values);
|
||||
}
|
||||
}
|
@@ -4,84 +4,84 @@ import { BehaviorSubject, from, merge, Observable, of, Subject } from 'rxjs';
|
||||
import { catchError, filter, finalize, map, mergeMap, switchMap, take, timeout } from 'rxjs/operators';
|
||||
|
||||
import {
|
||||
AddMyAuthFactorOTPRequest,
|
||||
AddMyAuthFactorOTPResponse,
|
||||
AddMyAuthFactorU2FRequest,
|
||||
AddMyAuthFactorU2FResponse,
|
||||
AddMyPasswordlessLinkRequest,
|
||||
AddMyPasswordlessLinkResponse,
|
||||
AddMyPasswordlessRequest,
|
||||
AddMyPasswordlessResponse,
|
||||
GetMyEmailRequest,
|
||||
GetMyEmailResponse,
|
||||
GetMyPasswordComplexityPolicyRequest,
|
||||
GetMyPasswordComplexityPolicyResponse,
|
||||
GetMyPhoneRequest,
|
||||
GetMyPhoneResponse,
|
||||
GetMyProfileRequest,
|
||||
GetMyProfileResponse,
|
||||
GetMyUserRequest,
|
||||
GetMyUserResponse,
|
||||
GetSupportedLanguagesRequest,
|
||||
GetSupportedLanguagesResponse,
|
||||
ListMyAuthFactorsRequest,
|
||||
ListMyAuthFactorsResponse,
|
||||
ListMyLinkedIDPsRequest,
|
||||
ListMyLinkedIDPsResponse,
|
||||
ListMyMembershipsRequest,
|
||||
ListMyMembershipsResponse,
|
||||
ListMyPasswordlessRequest,
|
||||
ListMyPasswordlessResponse,
|
||||
ListMyProjectOrgsRequest,
|
||||
ListMyProjectOrgsResponse,
|
||||
ListMyUserChangesRequest,
|
||||
ListMyUserChangesResponse,
|
||||
ListMyUserGrantsRequest,
|
||||
ListMyUserGrantsResponse,
|
||||
ListMyUserSessionsRequest,
|
||||
ListMyUserSessionsResponse,
|
||||
ListMyZitadelFeaturesRequest,
|
||||
ListMyZitadelFeaturesResponse,
|
||||
ListMyZitadelPermissionsRequest,
|
||||
ListMyZitadelPermissionsResponse,
|
||||
RemoveMyAuthFactorOTPRequest,
|
||||
RemoveMyAuthFactorOTPResponse,
|
||||
RemoveMyAuthFactorU2FRequest,
|
||||
RemoveMyAuthFactorU2FResponse,
|
||||
RemoveMyAvatarRequest,
|
||||
RemoveMyAvatarResponse,
|
||||
RemoveMyLinkedIDPRequest,
|
||||
RemoveMyLinkedIDPResponse,
|
||||
RemoveMyPasswordlessRequest,
|
||||
RemoveMyPasswordlessResponse,
|
||||
RemoveMyPhoneRequest,
|
||||
RemoveMyPhoneResponse,
|
||||
RemoveMyUserRequest,
|
||||
RemoveMyUserResponse,
|
||||
ResendMyEmailVerificationRequest,
|
||||
ResendMyEmailVerificationResponse,
|
||||
ResendMyPhoneVerificationRequest,
|
||||
ResendMyPhoneVerificationResponse,
|
||||
SendMyPasswordlessLinkRequest,
|
||||
SendMyPasswordlessLinkResponse,
|
||||
SetMyEmailRequest,
|
||||
SetMyEmailResponse,
|
||||
SetMyPhoneRequest,
|
||||
SetMyPhoneResponse,
|
||||
UpdateMyPasswordRequest,
|
||||
UpdateMyPasswordResponse,
|
||||
UpdateMyProfileRequest,
|
||||
UpdateMyProfileResponse,
|
||||
UpdateMyUserNameRequest,
|
||||
UpdateMyUserNameResponse,
|
||||
VerifyMyAuthFactorOTPRequest,
|
||||
VerifyMyAuthFactorOTPResponse,
|
||||
VerifyMyAuthFactorU2FRequest,
|
||||
VerifyMyAuthFactorU2FResponse,
|
||||
VerifyMyPasswordlessRequest,
|
||||
VerifyMyPasswordlessResponse,
|
||||
VerifyMyPhoneRequest,
|
||||
VerifyMyPhoneResponse,
|
||||
AddMyAuthFactorOTPRequest,
|
||||
AddMyAuthFactorOTPResponse,
|
||||
AddMyAuthFactorU2FRequest,
|
||||
AddMyAuthFactorU2FResponse,
|
||||
AddMyPasswordlessLinkRequest,
|
||||
AddMyPasswordlessLinkResponse,
|
||||
AddMyPasswordlessRequest,
|
||||
AddMyPasswordlessResponse,
|
||||
GetMyEmailRequest,
|
||||
GetMyEmailResponse,
|
||||
GetMyPasswordComplexityPolicyRequest,
|
||||
GetMyPasswordComplexityPolicyResponse,
|
||||
GetMyPhoneRequest,
|
||||
GetMyPhoneResponse,
|
||||
GetMyProfileRequest,
|
||||
GetMyProfileResponse,
|
||||
GetMyUserRequest,
|
||||
GetMyUserResponse,
|
||||
GetSupportedLanguagesRequest,
|
||||
GetSupportedLanguagesResponse,
|
||||
ListMyAuthFactorsRequest,
|
||||
ListMyAuthFactorsResponse,
|
||||
ListMyLinkedIDPsRequest,
|
||||
ListMyLinkedIDPsResponse,
|
||||
ListMyMembershipsRequest,
|
||||
ListMyMembershipsResponse,
|
||||
ListMyPasswordlessRequest,
|
||||
ListMyPasswordlessResponse,
|
||||
ListMyProjectOrgsRequest,
|
||||
ListMyProjectOrgsResponse,
|
||||
ListMyUserChangesRequest,
|
||||
ListMyUserChangesResponse,
|
||||
ListMyUserGrantsRequest,
|
||||
ListMyUserGrantsResponse,
|
||||
ListMyUserSessionsRequest,
|
||||
ListMyUserSessionsResponse,
|
||||
ListMyZitadelFeaturesRequest,
|
||||
ListMyZitadelFeaturesResponse,
|
||||
ListMyZitadelPermissionsRequest,
|
||||
ListMyZitadelPermissionsResponse,
|
||||
RemoveMyAuthFactorOTPRequest,
|
||||
RemoveMyAuthFactorOTPResponse,
|
||||
RemoveMyAuthFactorU2FRequest,
|
||||
RemoveMyAuthFactorU2FResponse,
|
||||
RemoveMyAvatarRequest,
|
||||
RemoveMyAvatarResponse,
|
||||
RemoveMyLinkedIDPRequest,
|
||||
RemoveMyLinkedIDPResponse,
|
||||
RemoveMyPasswordlessRequest,
|
||||
RemoveMyPasswordlessResponse,
|
||||
RemoveMyPhoneRequest,
|
||||
RemoveMyPhoneResponse,
|
||||
RemoveMyUserRequest,
|
||||
RemoveMyUserResponse,
|
||||
ResendMyEmailVerificationRequest,
|
||||
ResendMyEmailVerificationResponse,
|
||||
ResendMyPhoneVerificationRequest,
|
||||
ResendMyPhoneVerificationResponse,
|
||||
SendMyPasswordlessLinkRequest,
|
||||
SendMyPasswordlessLinkResponse,
|
||||
SetMyEmailRequest,
|
||||
SetMyEmailResponse,
|
||||
SetMyPhoneRequest,
|
||||
SetMyPhoneResponse,
|
||||
UpdateMyPasswordRequest,
|
||||
UpdateMyPasswordResponse,
|
||||
UpdateMyProfileRequest,
|
||||
UpdateMyProfileResponse,
|
||||
UpdateMyUserNameRequest,
|
||||
UpdateMyUserNameResponse,
|
||||
VerifyMyAuthFactorOTPRequest,
|
||||
VerifyMyAuthFactorOTPResponse,
|
||||
VerifyMyAuthFactorU2FRequest,
|
||||
VerifyMyAuthFactorU2FResponse,
|
||||
VerifyMyPasswordlessRequest,
|
||||
VerifyMyPasswordlessResponse,
|
||||
VerifyMyPhoneRequest,
|
||||
VerifyMyPhoneResponse,
|
||||
} from '../proto/generated/zitadel/auth_pb';
|
||||
import { ChangeQuery } from '../proto/generated/zitadel/change_pb';
|
||||
import { ListQuery } from '../proto/generated/zitadel/object_pb';
|
||||
@@ -98,10 +98,8 @@ export class GrpcAuthService {
|
||||
public user!: Observable<User.AsObject | undefined>;
|
||||
public userSubject: BehaviorSubject<User.AsObject | undefined> = new BehaviorSubject<User.AsObject | undefined>(undefined);
|
||||
private zitadelPermissions: BehaviorSubject<string[]> = new BehaviorSubject(['user.resourceowner']);
|
||||
private zitadelFeatures: BehaviorSubject<string[]> = new BehaviorSubject(['']);
|
||||
|
||||
public readonly fetchedZitadelPermissions: BehaviorSubject<boolean> = new BehaviorSubject(false as boolean);
|
||||
public readonly fetchedZitadelFeatures: BehaviorSubject<boolean> = new BehaviorSubject(false as boolean);
|
||||
|
||||
private cachedOrgs: Org.AsObject[] = [];
|
||||
|
||||
@@ -134,7 +132,6 @@ export class GrpcAuthService {
|
||||
}),
|
||||
finalize(() => {
|
||||
this.loadPermissions();
|
||||
this.loadFeatures();
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -142,7 +139,6 @@ export class GrpcAuthService {
|
||||
|
||||
this.activeOrgChanged.subscribe(() => {
|
||||
this.loadPermissions();
|
||||
this.loadFeatures();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -217,22 +213,6 @@ export class GrpcAuthService {
|
||||
});
|
||||
}
|
||||
|
||||
private loadFeatures(): void {
|
||||
from(this.listMyZitadelFeatures())
|
||||
.pipe(
|
||||
map((featuresResp) => featuresResp.resultList),
|
||||
catchError((_) => {
|
||||
return of([]);
|
||||
}),
|
||||
finalize(() => {
|
||||
this.fetchedZitadelFeatures.next(true);
|
||||
}),
|
||||
)
|
||||
.subscribe((features) => {
|
||||
this.zitadelFeatures.next(features);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* returns true if user has one of the provided roles
|
||||
* @param roles roles of the user
|
||||
@@ -262,35 +242,6 @@ export class GrpcAuthService {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* returns true if user has one of the provided features
|
||||
* @param features regex of the user
|
||||
*/
|
||||
public canUseFeature(features: string[] | RegExp[]): Observable<boolean> {
|
||||
if (features && features.length > 0) {
|
||||
return this.zitadelFeatures.pipe(switchMap((zFeatures) => of(this.hasFeature(zFeatures, features))));
|
||||
} else {
|
||||
return of(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* returns true if user has one of the provided features
|
||||
* @param userFeature features of the user
|
||||
* @param requestedFeature required features for accessing the respective component
|
||||
*/
|
||||
public hasFeature(userFeatures: string[], requestedFeatures: string[] | RegExp[]): boolean {
|
||||
return (
|
||||
requestedFeatures.findIndex((regexp: any) => {
|
||||
return (
|
||||
userFeatures.findIndex((feature) => {
|
||||
return new RegExp(regexp).test(feature);
|
||||
}) > -1
|
||||
);
|
||||
}) > -1
|
||||
);
|
||||
}
|
||||
|
||||
public getMyProfile(): Promise<GetMyProfileResponse.AsObject> {
|
||||
return this.grpcService.auth.getMyProfile(new GetMyProfileRequest(), null).then((resp) => resp.toObject());
|
||||
}
|
||||
|
@@ -6,16 +6,16 @@ import { BehaviorSubject, Observable, of, pairwise, Subject, takeUntil } from 'r
|
||||
import { KeyboardShortcutsComponent } from '../../modules/keyboard-shortcuts/keyboard-shortcuts.component';
|
||||
import { GrpcAuthService } from '../grpc-auth.service';
|
||||
import {
|
||||
ACTIONS,
|
||||
DOMAINS,
|
||||
HOME,
|
||||
KeyboardShortcut,
|
||||
ME,
|
||||
ORG,
|
||||
PROJECTS,
|
||||
SYSTEM,
|
||||
USERGRANTS,
|
||||
USERS,
|
||||
ACTIONS,
|
||||
DOMAINS,
|
||||
HOME,
|
||||
KeyboardShortcut,
|
||||
ME,
|
||||
ORG,
|
||||
PROJECTS,
|
||||
SYSTEM,
|
||||
USERGRANTS,
|
||||
USERS,
|
||||
} from './keyboard-shortcuts';
|
||||
|
||||
@Injectable({
|
||||
@@ -54,47 +54,47 @@ export class KeyboardShortcutsService implements OnDestroy {
|
||||
}
|
||||
|
||||
if (firstKey.code === 'KeyG' && secondKey.code === 'KeyH') {
|
||||
if (this.hasPermission(HOME) && this.hasFeature(HOME)) {
|
||||
if (this.hasPermission(HOME)) {
|
||||
this.router.navigate(HOME.link);
|
||||
}
|
||||
}
|
||||
if (firstKey.code === 'KeyG' && secondKey.code === 'KeyS') {
|
||||
if (this.hasPermission(SYSTEM) && this.hasFeature(SYSTEM)) {
|
||||
if (this.hasPermission(SYSTEM)) {
|
||||
this.router.navigate(SYSTEM.link);
|
||||
}
|
||||
}
|
||||
if (firstKey.code === 'KeyG' && secondKey.code === 'KeyO') {
|
||||
if (this.hasPermission(ORG) && this.hasFeature(ORG)) {
|
||||
if (this.hasPermission(ORG)) {
|
||||
this.router.navigate(ORG.link);
|
||||
}
|
||||
}
|
||||
if (firstKey.code === 'KeyM' && secondKey.code === 'KeyE') {
|
||||
if (this.hasPermission(ME) && this.hasFeature(ME)) {
|
||||
if (this.hasPermission(ME)) {
|
||||
this.router.navigate(ME.link);
|
||||
}
|
||||
}
|
||||
if (firstKey.code === 'KeyG' && secondKey.code === 'KeyP') {
|
||||
if (this.hasPermission(PROJECTS) && this.hasFeature(PROJECTS)) {
|
||||
if (this.hasPermission(PROJECTS)) {
|
||||
this.router.navigate(PROJECTS.link);
|
||||
}
|
||||
}
|
||||
if (firstKey.code === 'KeyG' && secondKey.code === 'KeyU') {
|
||||
if (this.hasPermission(USERS) && this.hasFeature(USERS)) {
|
||||
if (this.hasPermission(USERS)) {
|
||||
this.router.navigate(USERS.link);
|
||||
}
|
||||
}
|
||||
if (firstKey.code === 'KeyG' && secondKey.code === 'KeyA') {
|
||||
if (this.hasPermission(USERGRANTS) && this.hasFeature(USERGRANTS)) {
|
||||
if (this.hasPermission(USERGRANTS)) {
|
||||
this.router.navigate(USERGRANTS.link);
|
||||
}
|
||||
}
|
||||
if (firstKey.code === 'KeyG' && secondKey.code === 'KeyF') {
|
||||
if (this.hasPermission(ACTIONS) && this.hasFeature(ACTIONS)) {
|
||||
if (this.hasPermission(ACTIONS)) {
|
||||
this.router.navigate(ACTIONS.link);
|
||||
}
|
||||
}
|
||||
if (firstKey.code === 'KeyG' && secondKey.code === 'KeyD') {
|
||||
if (this.hasPermission(DOMAINS) && this.hasFeature(DOMAINS)) {
|
||||
if (this.hasPermission(DOMAINS)) {
|
||||
this.router.navigate(DOMAINS.link);
|
||||
}
|
||||
}
|
||||
@@ -136,12 +136,4 @@ export class KeyboardShortcutsService implements OnDestroy {
|
||||
return of(true);
|
||||
}
|
||||
}
|
||||
|
||||
private hasFeature(shortcut: KeyboardShortcut): Observable<boolean> {
|
||||
if (shortcut.features) {
|
||||
return this.authService.canUseFeature(shortcut.features);
|
||||
} else {
|
||||
return of(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -24,7 +24,6 @@ export interface CnslOverlay {
|
||||
requirements?: {
|
||||
media?: string;
|
||||
permission?: string[];
|
||||
feature?: string[];
|
||||
};
|
||||
}
|
||||
|
||||
@@ -83,10 +82,9 @@ export class OverlayWorkflowService implements OnDestroy {
|
||||
return zip([
|
||||
this.meetsMediaRequirements(overlay),
|
||||
this.meetsPermissionRequirements(overlay),
|
||||
this.meetsFeatureRequirements(overlay),
|
||||
] as Observable<boolean>[]).pipe(
|
||||
switchMap(([media, permission, feature]) => {
|
||||
return of(media && permission && feature);
|
||||
switchMap(([media, permission]) => {
|
||||
return of(media && permission);
|
||||
}),
|
||||
);
|
||||
}
|
||||
@@ -114,15 +112,6 @@ export class OverlayWorkflowService implements OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
public meetsFeatureRequirements(overlay: CnslOverlay): Observable<boolean> {
|
||||
const regexArray = overlay.requirements?.feature;
|
||||
if (regexArray && regexArray.length) {
|
||||
return this.authService.canUseFeature(regexArray);
|
||||
} else {
|
||||
return of(true);
|
||||
}
|
||||
}
|
||||
|
||||
public reset(): void {
|
||||
this.currentWorkflow$.next(null);
|
||||
if (this.callback) {
|
||||
|
@@ -21,7 +21,7 @@ export const IntroWorkflowOverlays: CnslOverlay[] = [
|
||||
content: {
|
||||
number: 2,
|
||||
count: 4,
|
||||
i18nText: 'OVERLAYS.SYSTEM.TEXT',
|
||||
i18nText: 'OVERLAYS.INSTANCE.TEXT',
|
||||
},
|
||||
requirements: {
|
||||
media: '(min-width: 600px)',
|
||||
|
@@ -4,34 +4,36 @@ import { Observable, of } from 'rxjs';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
export abstract class StatehandlerProcessorService {
|
||||
public abstract createState(url: string): Observable<string | undefined>;
|
||||
public abstract restoreState(state?: string): void;
|
||||
public abstract createState(url: string): Observable<string | undefined>;
|
||||
public abstract restoreState(state?: string): void;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class StatehandlerProcessorServiceImpl
|
||||
implements StatehandlerProcessorService {
|
||||
constructor(private location: Location) { }
|
||||
export class StatehandlerProcessorServiceImpl implements StatehandlerProcessorService {
|
||||
constructor(private location: Location) {}
|
||||
|
||||
public createState(url: string): Observable<string> {
|
||||
const externalUrl = this.location.prepareExternalUrl(url);
|
||||
const urlId = uuidv4();
|
||||
public createState(url: string): Observable<string> {
|
||||
const externalUrl = this.location.prepareExternalUrl(url);
|
||||
const urlId = uuidv4();
|
||||
|
||||
sessionStorage.setItem(urlId, externalUrl);
|
||||
|
||||
return of(urlId);
|
||||
if (externalUrl.includes('login_hint=')) {
|
||||
sessionStorage.setItem(urlId, externalUrl);
|
||||
}
|
||||
|
||||
public restoreState(state?: string): void {
|
||||
if (state === undefined) {
|
||||
return;
|
||||
}
|
||||
return of(urlId);
|
||||
}
|
||||
|
||||
const url = sessionStorage.getItem(state);
|
||||
if (url === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
window.location.href = window.location.origin + url;
|
||||
public restoreState(state?: string): void {
|
||||
if (state === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const url = sessionStorage.getItem(state);
|
||||
if (url === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
sessionStorage.removeItem(state);
|
||||
window.location.href = window.location.origin + url;
|
||||
}
|
||||
}
|
||||
|
@@ -1,59 +1,79 @@
|
||||
import { Injectable, Injector, OnDestroy, Type } from '@angular/core';
|
||||
import { GuardsCheckStart, Router, RouterEvent } from '@angular/router';
|
||||
import { OAuthService } from 'angular-oauth2-oidc';
|
||||
import { Observable, Subject, throwError } from 'rxjs';
|
||||
import { Observable, of, Subject, throwError } from 'rxjs';
|
||||
import { filter, map, shareReplay, switchMap, take, takeUntil } from 'rxjs/operators';
|
||||
|
||||
import { StatehandlerProcessorService } from './statehandler-processor.service';
|
||||
|
||||
export abstract class StatehandlerService {
|
||||
public abstract createState(): Observable<string | undefined>;
|
||||
public abstract initStateHandler(): void;
|
||||
public abstract createState(): Observable<string | undefined>;
|
||||
public abstract initStateHandler(): void;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class StatehandlerServiceImpl
|
||||
implements StatehandlerService, OnDestroy {
|
||||
private events?: Observable<string>;
|
||||
private unsubscribe$: Subject<void> = new Subject();
|
||||
export class StatehandlerServiceImpl implements StatehandlerService, OnDestroy {
|
||||
private events?: Observable<string>;
|
||||
private unsubscribe$: Subject<void> = new Subject();
|
||||
|
||||
constructor(
|
||||
oauthService: OAuthService,
|
||||
private injector: Injector,
|
||||
private processor: StatehandlerProcessorService,
|
||||
) {
|
||||
oauthService.events
|
||||
.pipe(
|
||||
filter(event => event.type === 'token_received'),
|
||||
map(() => oauthService.state),
|
||||
takeUntil(this.unsubscribe$),
|
||||
)
|
||||
.subscribe(state => processor.restoreState(state));
|
||||
constructor(oauthService: OAuthService, private injector: Injector, private processor: StatehandlerProcessorService) {
|
||||
oauthService.events
|
||||
.pipe(
|
||||
filter((event) => event.type === 'token_received'),
|
||||
map(() => oauthService.state),
|
||||
takeUntil(this.unsubscribe$),
|
||||
)
|
||||
.subscribe((state) => processor.restoreState(state));
|
||||
}
|
||||
|
||||
public initStateHandler(): void {
|
||||
const router = this.injector.get(Router as Type<Router>);
|
||||
this.events = (router.events as Observable<RouterEvent>).pipe(
|
||||
filter((event) => event instanceof GuardsCheckStart),
|
||||
map((event) => event.url),
|
||||
shareReplay(1),
|
||||
);
|
||||
|
||||
this.events.pipe(takeUntil(this.unsubscribe$)).subscribe();
|
||||
}
|
||||
|
||||
public createState(): Observable<string | undefined> {
|
||||
if (this.events === undefined) {
|
||||
return throwError(() => new Error('no router events'));
|
||||
}
|
||||
|
||||
public initStateHandler(): void {
|
||||
const router = this.injector.get(Router as Type<Router>);
|
||||
this.events = (router.events as Observable<RouterEvent>).pipe(
|
||||
filter(event => event instanceof GuardsCheckStart),
|
||||
map(event => event.url),
|
||||
shareReplay(1),
|
||||
);
|
||||
|
||||
this.events.pipe(takeUntil(this.unsubscribe$)).subscribe();
|
||||
}
|
||||
|
||||
public createState(): Observable<string | undefined> {
|
||||
if (this.events === undefined) {
|
||||
return throwError('no router events');
|
||||
return this.events.pipe(
|
||||
take(1),
|
||||
switchMap((url: string) => {
|
||||
if (url.includes('?login_hint=')) {
|
||||
const newUrl = this.removeParam('login_hint', url);
|
||||
return of(newUrl);
|
||||
} else {
|
||||
return this.processor.createState(url);
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
return this.events.pipe(
|
||||
take(1),
|
||||
switchMap(url => this.processor.createState(url)),
|
||||
);
|
||||
}
|
||||
public ngOnDestroy(): void {
|
||||
this.unsubscribe$.next();
|
||||
}
|
||||
|
||||
public ngOnDestroy(): void {
|
||||
this.unsubscribe$.next();
|
||||
removeParam(key: string, sourceURL: string) {
|
||||
var rtn = sourceURL.split('?')[0],
|
||||
param,
|
||||
params_arr = [],
|
||||
queryString = sourceURL.indexOf('?') !== -1 ? sourceURL.split('?')[1] : '';
|
||||
if (queryString !== '') {
|
||||
params_arr = queryString.split('&');
|
||||
for (var i = params_arr.length - 1; i >= 0; i -= 1) {
|
||||
param = params_arr[i].split('=')[0];
|
||||
if (param === key) {
|
||||
params_arr.splice(i, 1);
|
||||
}
|
||||
}
|
||||
if (params_arr.length) rtn = rtn + '?' + params_arr.join('&');
|
||||
}
|
||||
return rtn;
|
||||
}
|
||||
}
|
||||
|
@@ -1,98 +0,0 @@
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
import { StorageService } from './storage.service';
|
||||
|
||||
const authorizationKey = 'Authorization';
|
||||
const bearerPrefix = 'Bearer';
|
||||
const accessTokenStorageKey = 'access_token';
|
||||
|
||||
export interface StripeCustomer {
|
||||
contact: string;
|
||||
company?: string;
|
||||
address: string;
|
||||
city: string;
|
||||
postal_code: string;
|
||||
country: string;
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class SubscriptionService {
|
||||
constructor(private http: HttpClient, private storageService: StorageService) {}
|
||||
|
||||
public getLink(orgId: string, redirectURI: string): Promise<any> {
|
||||
return this.http
|
||||
.get('./assets/environment.json')
|
||||
.toPromise()
|
||||
.then((data: any) => {
|
||||
if (data && data.subscriptionServiceUrl) {
|
||||
const serviceUrl = data.subscriptionServiceUrl;
|
||||
const accessToken = this.storageService.getItem(accessTokenStorageKey);
|
||||
return this.http
|
||||
.get(`${serviceUrl}/redirect`, {
|
||||
headers: {
|
||||
[authorizationKey]: `${bearerPrefix} ${accessToken}`,
|
||||
},
|
||||
params: {
|
||||
org: orgId,
|
||||
return_url: encodeURI(redirectURI),
|
||||
country: 'ch',
|
||||
},
|
||||
})
|
||||
.toPromise();
|
||||
} else {
|
||||
return Promise.reject('Could not load environment');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public getCustomer(orgId: string): Promise<any> {
|
||||
return this.http
|
||||
.get('../../assets/environment.json')
|
||||
.toPromise()
|
||||
.then((data: any) => {
|
||||
if (data && data.subscriptionServiceUrl) {
|
||||
const serviceUrl = data.subscriptionServiceUrl;
|
||||
const accessToken = this.storageService.getItem(accessTokenStorageKey);
|
||||
return this.http
|
||||
.get(`${serviceUrl}/customer`, {
|
||||
headers: {
|
||||
[authorizationKey]: `${bearerPrefix} ${accessToken}`,
|
||||
},
|
||||
params: {
|
||||
org: orgId,
|
||||
},
|
||||
})
|
||||
.toPromise();
|
||||
} else {
|
||||
return Promise.reject('Could not load environment');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public setCustomer(orgId: string, body: StripeCustomer): Promise<any> {
|
||||
return this.http
|
||||
.get('./assets/environment.json')
|
||||
.toPromise()
|
||||
.then((data: any) => {
|
||||
if (data && data.subscriptionServiceUrl) {
|
||||
const serviceUrl = data.subscriptionServiceUrl;
|
||||
const accessToken = this.storageService.getItem(accessTokenStorageKey);
|
||||
return this.http
|
||||
.post(`${serviceUrl}/customer`, body, {
|
||||
headers: {
|
||||
[authorizationKey]: `${bearerPrefix} ${accessToken}`,
|
||||
},
|
||||
params: {
|
||||
org: orgId,
|
||||
},
|
||||
})
|
||||
.toPromise();
|
||||
} else {
|
||||
return Promise.reject('Could not load environment');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@@ -42,11 +42,11 @@
|
||||
}
|
||||
},
|
||||
"MENU": {
|
||||
"SYSTEM": "System",
|
||||
"INSTANCE": "Instance",
|
||||
"DASHBOARD": "Home",
|
||||
"PERSONAL_INFO": "Persönliche Informationen",
|
||||
"DOCUMENTATION": "Dokumentation",
|
||||
"IAM": "System Übersicht",
|
||||
"INSTANCEOVERVIEW": "Instance Übersicht",
|
||||
"VIEWS": "Views",
|
||||
"FAILEDEVENTS": "Failed Events",
|
||||
"ORGANIZATION": "Organisation",
|
||||
@@ -125,8 +125,8 @@
|
||||
"ORGSWITCHER": {
|
||||
"TEXT": "Alle Organisationseinstellungen und Tabellen basieren auf dieser ausgewählten Organisation. Klicken Sie auf diese Schaltfläche, um die Organisation zu wechseln oder eine neue zu erstellen."
|
||||
},
|
||||
"SYSTEM": {
|
||||
"TEXT": "Klicken Sie hier, um zu den Systemeinstellungen zu gelangen. Beachten Sie, dass Sie nur Zugriff auf diese Schaltfläche haben, wenn Sie über erweiterte Berechtigungen verfügen."
|
||||
"INSTANCE": {
|
||||
"TEXT": "Klicken Sie hier, um zu den Instanceeinstellungen zu gelangen. Beachten Sie, dass Sie nur Zugriff auf diese Schaltfläche haben, wenn Sie über erweiterte Berechtigungen verfügen."
|
||||
},
|
||||
"PROFILE": {
|
||||
"TEXT": "Hier können Sie zwischen Ihren Benutzerkonten wechseln und Ihre Sessions und Ihr Profil verwalten."
|
||||
@@ -794,74 +794,6 @@
|
||||
"SETPRIMARY": "Primäre Domain gesetzt."
|
||||
}
|
||||
},
|
||||
"FEATURES": {
|
||||
"TITLE": "Features",
|
||||
"DESCRIPTION": "Hier können Sie Funktionen von ZITADEL auf Basis von Ihrer Preisstufe einsehen.",
|
||||
"BTN-EDIT": "Featureset anzeigen",
|
||||
"AVAILABLE": "freigeschaltet",
|
||||
"UNAVAILABLE": "nicht freigeschaltet",
|
||||
"TIER": {
|
||||
"NAME": "Preisstufe Name",
|
||||
"DETAILS": "Abrechnungsdetails",
|
||||
"DETAILS_DESC": "Geben Sie hier Ihre Abrechnungsdaten ein.",
|
||||
"CUSTOMERINVALID": "Abrechnungsdaten sind nicht vollständig!",
|
||||
"CONTACT": "Kontakt",
|
||||
"COMPANY": "Name des Unternehmens",
|
||||
"ADDRESS": "Adresse",
|
||||
"CITY": "Ort",
|
||||
"POSTAL_CODE": "Postleitzahl",
|
||||
"COUNTRY": "Land",
|
||||
"TITLE": "Zitadel Preisstufe",
|
||||
"DESCRIPTION": "Zitadel arbeitet mit Stripe zusammen um die Rechnungsstellung zu vereinfachen. Der folgende Link leitet auf Stripe.com um.",
|
||||
"QUESTIONS": "Bei Fragen kontaktieren Sie unseren Support per Mail an ",
|
||||
"BTN": "Preisstufe ändern"
|
||||
},
|
||||
"HEADERS": {
|
||||
"LOGINPOLICY": "Login Einstellungen",
|
||||
"PASSWORD": "Passwort",
|
||||
"LABELPOLICY": "Privatelabelling",
|
||||
"DOMAIN": "Organisations Domänen",
|
||||
"TEXTSANDLINKS": "Texte und Links",
|
||||
"METADATA": "Metadata",
|
||||
"FLOWS": "Aktionen und Abläufe"
|
||||
},
|
||||
"DATA": {
|
||||
"AUDITLOGRETENTION": "Audit Log Retention",
|
||||
"LOGINPOLICYUSERNAMELOGIN": "Login mit Username erlauben",
|
||||
"LOGINPOLICYPASSWORDRESET": "Passwort vergessen Link nicht anzeigen",
|
||||
"LOGINPOLICYREGISTRATION": "Registration erlauben",
|
||||
"LOGINPOLICYIDP": "Identity Providers",
|
||||
"LOGINPOLICYFACTORS": "Multifaktoren",
|
||||
"LOGINPOLICYPASSWORDLESS": "Passwortlose Authentifizierung",
|
||||
"LOGINPOLICYCOMPLEXITYPOLICY": "Passwortkomplexitäts Richtlinie",
|
||||
"LOCKOUTPOLICY": "Passwortsperre",
|
||||
"LABELPOLICYPRIVATELABEL": "Farben, Logo, Icon und Texterscheinungsbild",
|
||||
"LABELPOLICYWATERMARK": "ZITADEL Wasserzeichen entfernen",
|
||||
"CUSTOMDOMAIN": "Domänverifikation",
|
||||
"CUSTOMTEXTLOGIN": "Benutzerdefinierte Logininterface Texte",
|
||||
"CUSTOMTEXTMESSAGE": "Benutzerdefinierte Benachrichtigungstexte",
|
||||
"PRIVACYPOLICY": "Benutzerdefinierte Datenschutzrichtlinie und AGB",
|
||||
"METADATAUSER": "User Metadata",
|
||||
"FLOWS": "Aktionen und Abläufe",
|
||||
"FLOW": {
|
||||
"ACTIONSALLOWED": {
|
||||
"0": "Nicht erlaubt",
|
||||
"1": "Limitierte Aktionen",
|
||||
"2": "Unlimitiert"
|
||||
},
|
||||
"TYPE": "Verwendungsart",
|
||||
"COUNT": "Anzahl"
|
||||
}
|
||||
},
|
||||
"TIERSTATES": {
|
||||
"0": "Aktiv",
|
||||
"1": "Aktion erforderlich",
|
||||
"2": "Annuliert",
|
||||
"3": "Durch IAM Owner gesetzt"
|
||||
},
|
||||
"NOTAVAILABLE": "Feature <strong>{{value}}</strong> ist auf Ihrer Organisation nicht freigeschaltet!",
|
||||
"RETENTIONDAYS": "Tage"
|
||||
},
|
||||
"POLICY": {
|
||||
"TITLE": "Richtlinen entdecken",
|
||||
"DESCRIPTION": "Vorgefertigte Richtlinien, die Dir Zeit sparen und die Sicherheit erhöhen.",
|
||||
@@ -1034,7 +966,7 @@
|
||||
"UPDATED": "Benutzerdefinierte Texte gespeichert."
|
||||
}
|
||||
},
|
||||
"DEFAULTLABEL": "Die aktuelle Richtlinie entspricht der IAM-Standard Einstellung.",
|
||||
"DEFAULTLABEL": "Die aktuelle Richtlinie entspricht der Instance-Standard Einstellung.",
|
||||
"BTN_INSTALL": "Installieren",
|
||||
"BTN_EDIT": "Modifizieren",
|
||||
"DATA": {
|
||||
|
@@ -42,11 +42,11 @@
|
||||
}
|
||||
},
|
||||
"MENU": {
|
||||
"SYSTEM": "System",
|
||||
"INSTANCE": "Instance",
|
||||
"DASHBOARD": "Home",
|
||||
"PERSONAL_INFO": "Personal Information",
|
||||
"DOCUMENTATION": "Documentation",
|
||||
"IAM": "System Overview",
|
||||
"INSTANCEOVERVIEW": "Instance Overview",
|
||||
"VIEWS": "Views",
|
||||
"FAILEDEVENTS": "Failed Events",
|
||||
"ORGANIZATION": "Organization",
|
||||
@@ -125,8 +125,8 @@
|
||||
"ORGSWITCHER": {
|
||||
"TEXT": "All organization settings and tables in console are based on a selected organization. Click this button to switch organization or create a new one."
|
||||
},
|
||||
"SYSTEM": {
|
||||
"TEXT": "Click here to get to the system settings. Note that you have only access to this button if you have enhanced permissions."
|
||||
"INSTANCE": {
|
||||
"TEXT": "Click here to get to the instance settings. Note that you have only access to this button if you have enhanced permissions."
|
||||
},
|
||||
"PROFILE": {
|
||||
"TEXT": "Here you can switch between your user accounts and manage your sessions and profile."
|
||||
@@ -794,74 +794,6 @@
|
||||
"SETPRIMARY": "Primary domain set."
|
||||
}
|
||||
},
|
||||
"FEATURES": {
|
||||
"TITLE": "Features",
|
||||
"DESCRIPTION": "Here you can see your ZITADEL Features based on your Tier.",
|
||||
"BTN-EDIT": "Display Featureset",
|
||||
"AVAILABLE": "unlocked",
|
||||
"UNAVAILABLE": "locked",
|
||||
"TIER": {
|
||||
"NAME": "Tier Name",
|
||||
"DETAILS": "Billing Details",
|
||||
"DETAILS_DESC": "Enter your billing data here.",
|
||||
"CUSTOMERINVALID": "Billing data ist not complete!",
|
||||
"CONTACT": "Kontakt",
|
||||
"COMPANY": "Company",
|
||||
"ADDRESS": "Addresse",
|
||||
"CITY": "Town",
|
||||
"POSTAL_CODE": "Postal Code",
|
||||
"COUNTRY": "Country",
|
||||
"TITLE": "Zitadel Tier",
|
||||
"DESCRIPTION": "Zitadel works with Stripe to simplify invoicing. The following link redirects to stripe.com.",
|
||||
"QUESTIONS": "For questions regarding payments contact our support ",
|
||||
"BTN": "Change Tier"
|
||||
},
|
||||
"HEADERS": {
|
||||
"LOGINPOLICY": "Login Settings",
|
||||
"PASSWORD": "Password",
|
||||
"LABELPOLICY": "Privatelabelling",
|
||||
"DOMAIN": "Organization Domain",
|
||||
"TEXTSANDLINKS": "Texts and Links",
|
||||
"METADATA": "Metadata",
|
||||
"FLOWS": "Actions and Flows"
|
||||
},
|
||||
"DATA": {
|
||||
"AUDITLOGRETENTION": "Audit Log Retention",
|
||||
"LOGINPOLICYUSERNAMELOGIN": "Allow login with Username",
|
||||
"LOGINPOLICYPASSWORDRESET": "Hide reset password link",
|
||||
"LOGINPOLICYREGISTRATION": "Allow self registration",
|
||||
"LOGINPOLICYIDP": "Identity Providers",
|
||||
"LOGINPOLICYFACTORS": "Multifactors",
|
||||
"LOGINPOLICYPASSWORDLESS": "Passwordless Authentication",
|
||||
"LOGINPOLICYCOMPLEXITYPOLICY": "Password Complexity Policy",
|
||||
"LOCKOUTPOLICY": "Lockout Policy",
|
||||
"LABELPOLICYPRIVATELABEL": "Colors, Logo, Icon, Textappearance",
|
||||
"LABELPOLICYWATERMARK": "Remove ZITADEL watermark",
|
||||
"CUSTOMDOMAIN": "Organization domain verification",
|
||||
"CUSTOMTEXTLOGIN": "Custom login interface texts",
|
||||
"CUSTOMTEXTMESSAGE": "Custom notification mail texts",
|
||||
"PRIVACYPOLICY": "Custom Privacy Policy and TOS Links",
|
||||
"METADATAUSER": "User Metadata",
|
||||
"FLOWS": "Actions and Flows",
|
||||
"FLOW": {
|
||||
"ACTIONSALLOWED": {
|
||||
"0": "Not allowed",
|
||||
"1": "Limited Actions",
|
||||
"2": "Unlimited Actions"
|
||||
},
|
||||
"TYPE": "Type of use",
|
||||
"COUNT": "Number"
|
||||
}
|
||||
},
|
||||
"TIERSTATES": {
|
||||
"0": "Active",
|
||||
"1": "Action required",
|
||||
"2": "Cancelled",
|
||||
"3": "Grandfathered"
|
||||
},
|
||||
"NOTAVAILABLE": "Feature <strong>{{value}}</strong> is missing on your organization.",
|
||||
"RETENTIONDAYS": "Days"
|
||||
},
|
||||
"POLICY": {
|
||||
"TITLE": "Explore Settings",
|
||||
"DESCRIPTION": "Pre-packaged settings that enhance your security.",
|
||||
|
@@ -42,11 +42,11 @@
|
||||
}
|
||||
},
|
||||
"MENU": {
|
||||
"SYSTEM": "Sistema",
|
||||
"INSTANCE": "Istanza",
|
||||
"DASHBOARD": "Pagina iniziale",
|
||||
"PERSONAL_INFO": "Informazioni personali",
|
||||
"DOCUMENTATION": "Documentazione",
|
||||
"IAM": "Panoramica Sistema",
|
||||
"INSTANCEOVERVIEW": "Panoramica Istanza",
|
||||
"VIEWS": "Views",
|
||||
"FAILEDEVENTS": "Eventi falliti",
|
||||
"ORGANIZATION": "Organizzazione",
|
||||
@@ -125,8 +125,8 @@
|
||||
"ORGSWITCHER": {
|
||||
"TEXT": "Tutte le impostazioni e le tabelle dell'organizzazione si basano su un'organizzazione selezionata. Fai clic sul pulsante per cambiare organizzazione o crearne una nuova."
|
||||
},
|
||||
"SYSTEM": {
|
||||
"TEXT": "Clicca qui per accedere alle impostazioni di sistema. Tieni presente che hai accesso solo se disponi di autorizzazioni avanzate."
|
||||
"INSTANCE": {
|
||||
"TEXT": "Clicca qui per accedere alle impostazioni dell'istanza. Tieni presente che hai accesso solo se disponi di autorizzazioni avanzate."
|
||||
},
|
||||
"PROFILE": {
|
||||
"TEXT": "Qui puoi passare da un account utente all'altro e gestire le sessioni e il profilo."
|
||||
@@ -794,74 +794,6 @@
|
||||
"SETPRIMARY": "Dominio primario cambiato con successo"
|
||||
}
|
||||
},
|
||||
"FEATURES": {
|
||||
"TITLE": "Features",
|
||||
"DESCRIPTION": "Qui puoi vedere le tue features in base al tuo Tier ZITADEL.",
|
||||
"BTN-EDIT": "Visualizza features",
|
||||
"AVAILABLE": "sbloccato",
|
||||
"UNAVAILABLE": "bloccato",
|
||||
"TIER": {
|
||||
"NAME": "Nome del Tier ZITADEL",
|
||||
"DETAILS": "Dettagli di fatturazione",
|
||||
"DETAILS_DESC": "Inserisci qui i tuoi dati di fatturazione.",
|
||||
"CUSTOMERINVALID": "I dati di fatturazione non sono completi!",
|
||||
"CONTACT": "Contatta",
|
||||
"COMPANY": "Azienda",
|
||||
"ADDRESS": "Indirizzo",
|
||||
"CITY": "Citt\u00e0",
|
||||
"POSTAL_CODE": "Codice postale",
|
||||
"COUNTRY": "Paese",
|
||||
"TITLE": "Tier Zitadel",
|
||||
"DESCRIPTION": "Zitadel lavora con Stripe per semplificare la fatturazione. Il seguente link reindirizza a stripe.com.",
|
||||
"QUESTIONS": "Per domande contatta il nostro supporto",
|
||||
"BTN": "Cambia Tier"
|
||||
},
|
||||
"HEADERS": {
|
||||
"LOGINPOLICY": "Impostazioni di accesso",
|
||||
"PASSWORD": "Password",
|
||||
"LABELPOLICY": "Privatelabelling",
|
||||
"DOMAIN": "Dominio dell'organizzazione",
|
||||
"TEXTSANDLINKS": "Testi e link",
|
||||
"METADATA": "Metadati",
|
||||
"FLOWS": "Azioni e processi"
|
||||
},
|
||||
"DATA": {
|
||||
"AUDITLOGRETENTION": "Ritenzione Audit Log",
|
||||
"LOGINPOLICYUSERNAMELOGIN": "Permettere l'accesso con il nome utente",
|
||||
"LOGINPOLICYPASSWORDRESET": "Nascondi il link per resettare la password",
|
||||
"LOGINPOLICYREGISTRATION": "Permettere l'auto-registrazione",
|
||||
"LOGINPOLICYIDP": "Identity Providers",
|
||||
"LOGINPOLICYFACTORS": "Multifattori",
|
||||
"LOGINPOLICYPASSWORDLESS": "Autenticazione passwordless",
|
||||
"LOGINPOLICYCOMPLEXITYPOLICY": "Impostazioni di complessit\u00e0 delle password",
|
||||
"LOCKOUTPOLICY": "Impostazioni di blocco",
|
||||
"LABELPOLICYPRIVATELABEL": "Colori, logo, icona, aspetto del testo",
|
||||
"LABELPOLICYWATERMARK": "Rimuovi la filigrana ZITADEL",
|
||||
"CUSTOMDOMAIN": "Verifica del dominio dell'organizzazione",
|
||||
"CUSTOMTEXTLOGIN": "Testi dell'interfaccia login",
|
||||
"CUSTOMTEXTMESSAGE": "Testi email personalizzati",
|
||||
"PRIVACYPOLICY": "Link personalizzati all'informativa sulla privacy e ai TOS",
|
||||
"METADATAUSER": "Metadati utente",
|
||||
"FLOWS": "Azioni e processi",
|
||||
"FLOW": {
|
||||
"ACTIONSALLOWED": {
|
||||
"0": "Non abilitato",
|
||||
"1": "Numero limitato",
|
||||
"2": "Illimitato"
|
||||
},
|
||||
"COUNT": "Anzahl",
|
||||
"TYPE": "Tipo di utilizzo"
|
||||
}
|
||||
},
|
||||
"TIERSTATES": {
|
||||
"0": "Attivo",
|
||||
"1": "Azione richiesta",
|
||||
"2": "Cancellato",
|
||||
"3": "Grandfathered"
|
||||
},
|
||||
"NOTAVAILABLE": "La feature <strong>{{value}}</strong> manca nella tua organizzazione.",
|
||||
"RETENTIONDAYS": "Giorni"
|
||||
},
|
||||
"POLICY": {
|
||||
"TITLE": "Esplora le impostazioni",
|
||||
"DESCRIPTION": "Impostazioni che migliorano la tua sicurezza.",
|
||||
|
@@ -28,6 +28,7 @@
|
||||
@import 'src/app/pages/users/user-detail/auth-user-detail/auth-user-detail.component';
|
||||
@import 'src/app/pages/users/user-detail/user-detail/user-detail.component';
|
||||
@import 'src/app/pages/users/user-list/user-table/user-table.component';
|
||||
@import 'src/app/pages/users/user-detail/contact/contact.component';
|
||||
@import 'src/app/pages/projects/project-grid/project-grid.component';
|
||||
@import 'src/app/app.component.scss';
|
||||
@import './styles/color.scss';
|
||||
@@ -39,7 +40,6 @@
|
||||
@import 'src/app/pages/projects/owned-projects/project-grant-detail/project-grant-illustration/project-grant-illustration.component';
|
||||
@import 'src/app/modules/accounts-card/accounts-card.component.scss';
|
||||
@import 'src/app/modules/filter/filter.component.scss';
|
||||
@import 'src/app/modules/zitadel-tier/zitadel-tier.component.scss';
|
||||
@import 'src/app/modules/policies/message-texts/message-texts.component.scss';
|
||||
@import 'src/app/modules/policies/private-labeling-policy/private-labeling-policy.component.scss';
|
||||
@import 'src/app/modules/policies/private-labeling-policy/preview/preview.component.scss';
|
||||
@@ -101,7 +101,6 @@
|
||||
@include accounts-card-theme($theme);
|
||||
@include sidenav-theme($theme);
|
||||
@include info-section-theme($theme);
|
||||
@include tier-theme($theme);
|
||||
@include filter-theme($theme);
|
||||
@include preview-theme($theme);
|
||||
@include private-label-theme($theme);
|
||||
@@ -112,4 +111,5 @@
|
||||
@include action-dialog-theme($theme);
|
||||
@include action-keys-theme($theme);
|
||||
@include codemirror-theme($theme);
|
||||
@include contact-theme($theme);
|
||||
}
|
||||
|
@@ -421,6 +421,17 @@ $custom-typography: mat.define-typography-config(
|
||||
color: map-get($foreground, base);
|
||||
}
|
||||
|
||||
.mat-menu-panel {
|
||||
background-color: map-get($background, cards);
|
||||
transition: background-color 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.mat-button-toggle-button {
|
||||
background-color: mat.get-color-from-palette($background, cards);
|
||||
transition: background-color 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
|
||||
}
|
||||
|
||||
.main-container,
|
||||
.mat-dialog-container {
|
||||
background-color: map-get($background, background);
|
||||
@@ -464,6 +475,17 @@ $custom-typography: mat.define-typography-config(
|
||||
color: map-get($foreground, text);
|
||||
}
|
||||
|
||||
.mat-menu-panel {
|
||||
background-color: map-get($background, cards);
|
||||
transition: background-color 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.mat-button-toggle-button {
|
||||
background-color: mat.get-color-from-palette($background, cards);
|
||||
transition: background-color 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
|
||||
}
|
||||
|
||||
.main-container,
|
||||
.mat-dialog-container {
|
||||
background-color: map-get($background, background);
|
||||
|
Reference in New Issue
Block a user