diff --git a/cmd/zitadel/setup.yaml b/cmd/zitadel/setup.yaml index c3a4fcfb9d..0643eae9ee 100644 --- a/cmd/zitadel/setup.yaml +++ b/cmd/zitadel/setup.yaml @@ -83,7 +83,7 @@ SetUp: DefaultLabelPolicy: PrimaryColor: '#5282c1' BackgroundColor: '#141735' - WarnColor: '#f44336' + WarnColor: '#ff3b5b' FontColor: '#ffffff' Step7: OTP: true diff --git a/console/src/app/app.component.scss b/console/src/app/app.component.scss index 786531dd61..f98547b0ae 100644 --- a/console/src/app/app.component.scss +++ b/console/src/app/app.component.scss @@ -275,7 +275,7 @@ .show-all { $primary: map-get($theme, primary); color: mat.get-color-from-palette($primary, 300) !important; - border-bottom: 2px solid var(--grey); + border-bottom: 1px solid var(--grey); margin-bottom: .5rem; } /* stylelint-enable */ diff --git a/console/src/app/app.component.ts b/console/src/app/app.component.ts index a0e815d0e2..15a7a2717e 100644 --- a/console/src/app/app.component.ts +++ b/console/src/app/app.component.ts @@ -242,8 +242,8 @@ export class AppComponent implements OnDestroy { const darkPrimary = '#5282c1'; const lightPrimary = '#5282c1'; - const darkWarn = '#F44336'; - const lightWarn = '#F44336'; + const darkWarn = '#cd3d56'; + const lightWarn = '#cd3d56'; const darkBackground = '#212224'; const lightBackground = '#fafafa'; @@ -267,8 +267,8 @@ export class AppComponent implements OnDestroy { const darkPrimary = this.labelpolicy?.primaryColorDark || '#5282c1'; const lightPrimary = this.labelpolicy?.primaryColor || '#5282c1'; - const darkWarn = this.labelpolicy?.warnColorDark || '#F44336'; - const lightWarn = this.labelpolicy?.warnColor || '#F44336'; + const darkWarn = this.labelpolicy?.warnColorDark || '#cd3d56'; + const lightWarn = this.labelpolicy?.warnColor || '#cd3d56'; const darkBackground = this.labelpolicy?.backgroundColorDark || '#212224'; const lightBackground = this.labelpolicy?.backgroundColor || '#fafafa'; diff --git a/console/src/app/directives/has-feature/has-feature.directive.ts b/console/src/app/directives/has-feature/has-feature.directive.ts new file mode 100644 index 0000000000..7cdf22bcb1 --- /dev/null +++ b/console/src/app/directives/has-feature/has-feature.directive.ts @@ -0,0 +1,30 @@ +import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core'; +import { GrpcAuthService } from 'src/app/services/grpc-auth.service'; + + +@Directive({ + selector: '[appHasFeature]', +}) + +export class HasFeatureDirective { + private hasView: boolean = false; + @Input() public set appHasFeature(features: string[] | RegExp[]) { + if (features && features.length > 0) { + this.authService.canUseFeature(features).subscribe(isAllowed => { + if (isAllowed && !this.hasView) { + this.viewContainerRef.clear(); + this.viewContainerRef.createEmbeddedView(this.templateRef); + } else if (this.hasView) { + this.viewContainerRef.clear(); + this.hasView = false; + } + }); + } + } + + constructor( + private authService: GrpcAuthService, + protected templateRef: TemplateRef, + protected viewContainerRef: ViewContainerRef, + ) { } +} diff --git a/console/src/app/directives/has-feature/has-feature.module.ts b/console/src/app/directives/has-feature/has-feature.module.ts new file mode 100644 index 0000000000..b0a5d68b77 --- /dev/null +++ b/console/src/app/directives/has-feature/has-feature.module.ts @@ -0,0 +1,19 @@ +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 { } diff --git a/console/src/app/modules/detail-layout/detail-layout.component.scss b/console/src/app/modules/detail-layout/detail-layout.component.scss index 2fc80e263c..c9db8863ee 100644 --- a/console/src/app/modules/detail-layout/detail-layout.component.scss +++ b/console/src/app/modules/detail-layout/detail-layout.component.scss @@ -11,16 +11,24 @@ .detail-container { display: flex; + flex-direction: column; padding-bottom: 3rem; + @media only screen and (min-width: 550px) { + flex-direction: row; + } + .detail-left { align-self: flex-start; - width: 100px; display: flex; padding: 1rem; padding-top: 0; justify-content: center; + @media only screen and (min-width: 550px) { + width: 100px; + } + a { margin-top: 13px; color: inherit; diff --git a/console/src/app/modules/features/features.component.html b/console/src/app/modules/features/features.component.html index a64e3cdd9a..2f959d7808 100644 --- a/console/src/app/modules/features/features.component.html +++ b/console/src/app/modules/features/features.component.html @@ -58,126 +58,212 @@ translate}} -
+

{{'FEATURES.HEADERS.LOGINPOLICY' | translate}}

- +
+ +
{{'FEATURES.DATA.LOGINPOLICYUSERNAMELOGIN' | translate}} - + +
- +
+ +
{{'FEATURES.DATA.LOGINPOLICYPASSWORDRESET' | translate}} - - + + +
- +
+ +
{{'FEATURES.DATA.LOGINPOLICYREGISTRATION' | translate}} - + +
- +
+ +
{{'FEATURES.DATA.LOGINPOLICYIDP' | translate}} - + +
- +
+ +
{{'FEATURES.DATA.LOGINPOLICYFACTORS' | translate}} - + +
- +
+ +
{{'FEATURES.DATA.LOGINPOLICYPASSWORDLESS' | translate}} - + +
-
+

{{'FEATURES.HEADERS.PASSWORD' | translate}}

- +
+ +
{{'FEATURES.DATA.LOGINPOLICYCOMPLEXITYPOLICY' | translate}} - + +
-
+
+
+ +
+ + {{'FEATURES.DATA.LOCKOUTPOLICY' | translate}} + + + + +
+ +

{{'FEATURES.HEADERS.LABELPOLICY' | translate}}

- +
+ +
{{'FEATURES.DATA.LABELPOLICYPRIVATELABEL' | translate}} - + +
- +
+ +
{{'FEATURES.DATA.LABELPOLICYWATERMARK' | translate}} - + +
-
+

{{'FEATURES.HEADERS.DOMAIN' | translate}}

- +
+ +
{{'FEATURES.DATA.CUSTOMDOMAIN' | translate}} - + + + +
+ +

{{'FEATURES.HEADERS.TEXTSANDLINKS' | translate}}

+ +
+
+ +
+ {{'FEATURES.DATA.CUSTOMTEXTMESSAGE' | translate}} + + +
- +
+ +
+ {{'FEATURES.DATA.CUSTOMTEXTLOGIN' | translate}} + + + + +
+ +
+
+ +
{{'FEATURES.DATA.PRIVACYPOLICY' | translate}} - + +
+

{{'FEATURES.HEADERS.METADATA' | translate}}

+
- - {{'FEATURES.DATA.CUSTOMTEXT' | translate}} +
+ +
+ {{'FEATURES.DATA.METADATAUSER' | translate}} - + +
@@ -188,3 +274,9 @@ }} + + + + {{active ? ('FEATURES.AVAILABLE' | translate) : ('FEATURES.UNAVAILABLE' | translate)}} + + \ No newline at end of file diff --git a/console/src/app/modules/features/features.component.scss b/console/src/app/modules/features/features.component.scss index 4c310fec53..24f64aa97a 100644 --- a/console/src/app/modules/features/features.component.scss +++ b/console/src/app/modules/features/features.component.scss @@ -46,7 +46,7 @@ } .error { - color: #f44336; + color: var(--warn); font-size: 14px; } @@ -71,19 +71,70 @@ flex-direction: column; width: 100%; + .feature-section { + font-size: 14px; + color: var(--grey); + margin-top: 1.5rem; + } + .row { display: flex; align-items: center; padding: .3rem 0; - i, - .icon { + .featureavatar { margin-right: 1rem; - font-size: 1.5rem; + 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); + } + + &.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: .9rem; + margin-right: 1rem; } .fill-space { @@ -107,3 +158,7 @@ display: block; } } + +.toggle { + margin-left: 1rem; +} diff --git a/console/src/app/modules/features/features.component.ts b/console/src/app/modules/features/features.component.ts index 468d333082..3ec921e809 100644 --- a/console/src/app/modules/features/features.component.ts +++ b/console/src/app/modules/features/features.component.ts @@ -160,8 +160,11 @@ export class FeaturesComponent implements OnDestroy { req.setLabelPolicyPrivateLabel(this.features.labelPolicyPrivateLabel); req.setLabelPolicyWatermark(this.features.labelPolicyWatermark); req.setCustomDomain(this.features.customDomain); - req.setCustomText(this.features.customText); + 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); this.adminService.setOrgFeatures(req).then(() => { this.toast.showInfo('POLICY.TOAST.SET', true); @@ -182,7 +185,10 @@ export class FeaturesComponent implements OnDestroy { dreq.setLabelPolicyPrivateLabel(this.features.labelPolicyPrivateLabel); dreq.setLabelPolicyWatermark(this.features.labelPolicyWatermark); dreq.setCustomDomain(this.features.customDomain); - dreq.setCustomText(this.features.customText); + dreq.setCustomTextLogin(this.features.customTextLogin); + dreq.setCustomTextMessage(this.features.customTextMessage); + dreq.setMetadataUser(this.features.metadataUser); + dreq.setLockoutPolicy(this.features.lockoutPolicy); this.adminService.setDefaultFeatures(dreq).then(() => { this.toast.showInfo('POLICY.TOAST.SET', true); diff --git a/console/src/app/modules/info-row/info-row.component.html b/console/src/app/modules/info-row/info-row.component.html new file mode 100644 index 0000000000..51ddb2f0c7 --- /dev/null +++ b/console/src/app/modules/info-row/info-row.component.html @@ -0,0 +1,82 @@ +
+
+

{{ 'USER.PAGES.STATE' | translate }}

+

{{'USER.DATA.STATE'+user.state + | translate}}

+
+ +
+

{{ 'USER.DETAILS.DATECREATED' | translate }}

+

{{user?.details?.creationDate | timestampToDate | localizedDate: 'dd. MMMM YYYY, HH:mm' }}

+
+ +
+

{{ 'USER.DETAILS.DATECHANGED' | translate }}

+

{{user?.details?.changeDate | timestampToDate | localizedDate: 'dd. MMMM YYYY, HH:mm' }}

+
+ +
+

{{ 'USER.PAGES.LOGINNAMES' | translate }}

+
+ +
+
+
+ +
+
+

{{ 'APP.PAGES.STATE' | translate }}

+

{{'APP.PAGES.DETAIL.STATE.'+app.state + | translate}}

+
+ +
+

{{ 'APP.PAGES.DATECREATED' | translate }}

+

{{app?.details?.creationDate | timestampToDate | localizedDate: 'dd. MMMM YYYY, HH:mm' }}

+
+ +
+

{{ 'APP.PAGES.DATECHANGED' | translate }}

+

{{app?.details?.changeDate | timestampToDate | localizedDate: 'dd. MMMM YYYY, HH:mm' }}

+
+ +
+

{{ 'APP.OIDC.INFO.CLIENTID' | translate }}

+
+ +
+ +
+ +
+
+ +
+

{{ 'APP.PAGES.URLS' | translate }}

+
+
+ {{environmentV.key}} + +
+
+
+
\ No newline at end of file diff --git a/console/src/app/modules/info-row/info-row.component.scss b/console/src/app/modules/info-row/info-row.component.scss new file mode 100644 index 0000000000..6d9425ebac --- /dev/null +++ b/console/src/app/modules/info-row/info-row.component.scss @@ -0,0 +1,77 @@ +@use '~@angular/material' as mat; + +@mixin info-row-theme($theme) { + $foreground: map-get($theme, foreground); + $button-text-color: map-get($foreground, text); + $button-disabled-text-color: map-get($foreground, disabled-button); + + .info-row { + display: flex; + flex-direction: column; + margin: 0 -.5rem; + + @media only screen and (min-width: 500px) { + flex-direction: row; + flex-wrap: wrap; + } + + .info { + display: flex; + flex-direction: column; + margin: .5rem .5rem; + flex: 1; + align-items: flex-start; + box-sizing: border-box; + + &:not(.width) { + min-width: 100px; + } + + .title { + font-size: 14px; + color: var(--grey); + margin: 0; + } + + .desc { + margin: .5rem 0; + font-size: 14px; + padding: 2px 0; + } + + .copy-row { + display: flex; + flex-direction: column; + width: 100%; + align-items: stretch; + + button { + transition: opacity .15s ease-in-out; + background-color: #8795a110; + border: 1px solid #8795a160; + border-radius: 4px; + padding: .25rem 1rem; + margin: .25rem 0; + color: $button-text-color; + text-overflow: ellipsis; + overflow: hidden; + + &[disabled] { + color: $button-disabled-text-color; + } + } + + .environment { + display: flex; + flex-direction: column; + width: 100%; + margin: .25rem 0; + + .key { + font-size: 14px; + } + } + } + } + } +} diff --git a/console/src/app/modules/info-row/info-row.component.spec.ts b/console/src/app/modules/info-row/info-row.component.spec.ts new file mode 100644 index 0000000000..2e31c5bd2e --- /dev/null +++ b/console/src/app/modules/info-row/info-row.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { InfoRowComponent } from './info-row.component'; + +describe('InfoRowComponent', () => { + let component: InfoRowComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [InfoRowComponent], + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(InfoRowComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/console/src/app/modules/info-row/info-row.component.ts b/console/src/app/modules/info-row/info-row.component.ts new file mode 100644 index 0000000000..67e5051df4 --- /dev/null +++ b/console/src/app/modules/info-row/info-row.component.ts @@ -0,0 +1,36 @@ +import { HttpClient } from '@angular/common/http'; +import { Component, Input, OnInit } from '@angular/core'; +import { App, AppState } from 'src/app/proto/generated/zitadel/app_pb'; +import { User, UserState } from 'src/app/proto/generated/zitadel/user_pb'; + +@Component({ + selector: 'cnsl-info-row', + templateUrl: './info-row.component.html', + styleUrls: ['./info-row.component.scss'], +}) +export class InfoRowComponent implements OnInit { + @Input() public user!: User.AsObject; + @Input() public app!: App.AsObject; + public UserState: any = UserState; + public AppState: any = AppState; + public copied: string = ''; + + public environmentMap: { [key: string]: string; } = {}; + + constructor(private http: HttpClient) { } + + ngOnInit(): void { + if (this.app) { + this.http.get('./assets/environment.json') + .toPromise().then((env: any) => { + this.environmentMap = { + issuer: env.issuer, + adminServiceUrl: env.adminServiceUrl, + mgmtServiceUrl: env.mgmtServiceUrl, + authServiceUrl: env.adminServiceUrl, + }; + }); + } + } + +} diff --git a/console/src/app/modules/info-row/info-row.module.ts b/console/src/app/modules/info-row/info-row.module.ts new file mode 100644 index 0000000000..886dd70887 --- /dev/null +++ b/console/src/app/modules/info-row/info-row.module.ts @@ -0,0 +1,33 @@ +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { MatButtonModule } from '@angular/material/button'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { TranslateModule } from '@ngx-translate/core'; +import { CopyToClipboardModule } from 'src/app/directives/copy-to-clipboard/copy-to-clipboard.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'; + +import { InfoRowComponent } from './info-row.component'; + + + +@NgModule({ + declarations: [ + InfoRowComponent, + ], + imports: [ + CommonModule, + FormsModule, + MatTooltipModule, + TranslateModule, + CopyToClipboardModule, + MatButtonModule, + LocalizedDatePipeModule, + TimestampToDatePipeModule, + ], + exports: [ + InfoRowComponent, + ], +}) +export class InfoRowModule { } diff --git a/console/src/app/modules/info-section/info-section.component.html b/console/src/app/modules/info-section/info-section.component.html index 4de828cb37..42b3912cd0 100644 --- a/console/src/app/modules/info-section/info-section.component.html +++ b/console/src/app/modules/info-section/info-section.component.html @@ -5,4 +5,9 @@
+ + + {{'ACTIONS.GOTOFEATURES' | translate}} + + \ No newline at end of file diff --git a/console/src/app/modules/info-section/info-section.component.scss b/console/src/app/modules/info-section/info-section.component.scss index dd478bd42d..9c92ed6b4b 100644 --- a/console/src/app/modules/info-section/info-section.component.scss +++ b/console/src/app/modules/info-section/info-section.component.scss @@ -13,6 +13,7 @@ padding: .5rem 0; padding-right: 1rem; font-size: 14px; + margin: .5rem 0; .icon { margin-right: 1rem; @@ -20,10 +21,29 @@ line-height: 1.2rem; font-size: 1.2rem; margin-left: .5rem; + padding: .25rem 0; } .info-section-content { flex: 1; + padding: .25rem 0; + } + + .action { + font-size: 14px; + display: flex; + align-items: center; + text-decoration: none; + margin-left: .5rem; + border-radius: 50vw; + align-self: center; + padding: .25rem .5rem; + background: if($is-dark-theme, #00000030, #ffffff40); + font-weight: 600; + + i { + font-size: 1.2rem; + } } &.info { diff --git a/console/src/app/modules/info-section/info-section.component.ts b/console/src/app/modules/info-section/info-section.component.ts index 0126c8011b..938bb26700 100644 --- a/console/src/app/modules/info-section/info-section.component.ts +++ b/console/src/app/modules/info-section/info-section.component.ts @@ -14,4 +14,5 @@ enum InfoSectionType { export class InfoSectionComponent { @Input() type: InfoSectionType = InfoSectionType.INFO; + @Input() featureLink: string = ''; } diff --git a/console/src/app/modules/info-section/info-section.module.ts b/console/src/app/modules/info-section/info-section.module.ts index 894e0b250b..c0b84382fe 100644 --- a/console/src/app/modules/info-section/info-section.module.ts +++ b/console/src/app/modules/info-section/info-section.module.ts @@ -1,17 +1,21 @@ import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; +import { RouterModule } from '@angular/router'; +import { TranslateModule } from '@ngx-translate/core'; import { InfoSectionComponent } from './info-section.component'; @NgModule({ - declarations: [InfoSectionComponent], - imports: [ - CommonModule, - ], - exports: [ - InfoSectionComponent, - ], + declarations: [InfoSectionComponent], + imports: [ + CommonModule, + TranslateModule, + RouterModule, + ], + exports: [ + InfoSectionComponent, + ], }) export class InfoSectionModule { } diff --git a/console/src/app/modules/links/links.component.html b/console/src/app/modules/links/links.component.html index af0435b761..cacd027c0f 100644 --- a/console/src/app/modules/links/links.component.html +++ b/console/src/app/modules/links/links.component.html @@ -1,26 +1,35 @@
+
{{'NEXTSTEPS.TITLE' | translate}}
+ +
-
-
{{ link.i18nTitle | translate }}
-

{{link.i18nDesc | translate}}

- - - -
-
-
+
+
{{ link.i18nTitle | translate }}

{{link.i18nDesc | translate}}

- - + + {{'ACTIONS.CONTINUE' | translate}} + + + {{'ACTIONS.CONTINUE' | translate}} + +
+ +
+ +
{{ link.i18nTitle | translate }}
+

{{link.i18nDesc | translate}}

+ + + {{'ACTIONS.CONTINUE' | translate}} + + + {{'ACTIONS.CONTINUE' | translate}} +
diff --git a/console/src/app/modules/links/links.component.scss b/console/src/app/modules/links/links.component.scss index cf96272ca9..8843220e56 100644 --- a/console/src/app/modules/links/links.component.scss +++ b/console/src/app/modules/links/links.component.scss @@ -1,45 +1,61 @@ .next-steps { - margin-top: 1rem; + margin-top: 4rem; - h5 { - text-transform: uppercase; - font-size: 14px; - color: var(--grey); + .title-row { + display: flex; + align-items: center; + + h5 { + text-transform: uppercase; + font-size: 14px; + letter-spacing: .05em; + font-weight: 400; + margin-right: 1rem; + } } .row { - display: flex; - overflow-x: auto; - flex-wrap: wrap; + display: grid; + row-gap: 1rem; + column-gap: 1rem; + grid-template-columns: 1fr 1fr 1fr; padding-bottom: .5rem; - margin: 0 -.5rem; - margin-bottom: 2rem; + + @media only screen and (max-width: 1300px) { + grid-template-columns: 1fr 1fr; + } + + @media only screen and (max-width: 450px) { + grid-template-columns: 1fr; + } .step { - min-width: 220px; padding: 1rem; - margin: 1rem .5rem; - border: 1px solid #8795a150; - border-radius: .5rem; display: flex; flex-direction: column; - align-items: center; box-sizing: border-box; + align-items: flex-start; flex: 1; @media only screen and (min-width: 899px) { max-width: 280px; } + i { + font-size: 2.5rem; + margin-bottom: 1rem; + } + h6 { - font-size: 1rem; + font-size: 1.1rem; text-align: center; - margin: 0 0 1rem 0; + font-weight: 400; + margin: 0; } p { font-size: 14px; - text-align: center; + margin: 1rem 0; color: var(--grey); } diff --git a/console/src/app/modules/links/links.component.ts b/console/src/app/modules/links/links.component.ts index b9eaf2be51..0aafd44944 100644 --- a/console/src/app/modules/links/links.component.ts +++ b/console/src/app/modules/links/links.component.ts @@ -2,23 +2,24 @@ import { Component, Input, OnInit } from '@angular/core'; export interface CnslLinks { - i18nTitle: string; - i18nDesc: string; - routerLink?: any; - href?: string; - withRole?: Array; + i18nTitle: string; + i18nDesc: string; + routerLink?: any; + href?: string; + iconClasses?: string; + withRole?: Array; } @Component({ - selector: 'cnsl-links', - templateUrl: './links.component.html', - styleUrls: ['./links.component.scss'], + selector: 'cnsl-links', + templateUrl: './links.component.html', + styleUrls: ['./links.component.scss'], }) export class LinksComponent implements OnInit { - @Input() links: Array = []; - constructor() { } + @Input() links: Array = []; + constructor() { } - ngOnInit(): void { - } + ngOnInit(): void { + } } diff --git a/console/src/app/modules/meta-layout/meta-layout.component.scss b/console/src/app/modules/meta-layout/meta-layout.component.scss index e168359e37..900ca3c228 100644 --- a/console/src/app/modules/meta-layout/meta-layout.component.scss +++ b/console/src/app/modules/meta-layout/meta-layout.component.scss @@ -8,7 +8,7 @@ display: relative; width: 100%; overflow-y: auto; - padding-bottom: 50px; + padding-bottom: 2rem; &.hidden { flex-basis: 100%; diff --git a/console/src/app/modules/meta-layout/meta.scss b/console/src/app/modules/meta-layout/meta.scss index 4ff4da3660..c3d79b8cbb 100644 --- a/console/src/app/modules/meta-layout/meta.scss +++ b/console/src/app/modules/meta-layout/meta.scss @@ -3,9 +3,29 @@ .meta-details { margin-bottom: 1rem; - border-bottom: 1px solid #81868a40; padding-bottom: 1rem; + .title-row { + display: flex; + align-items: center; + margin: .5rem 0; + + .title { + font-size: 14px; + letter-spacing: .05em; + text-transform: uppercase; + display: block; + } + + .edit { + font-size: 14px; + } + + .fill-space { + flex: 1; + } + } + .meta-row { display: flex; margin-bottom: .5rem; diff --git a/console/src/app/modules/mfa-table/mfa-table.component.scss b/console/src/app/modules/mfa-table/mfa-table.component.scss index fda07728bc..dc481a67d7 100644 --- a/console/src/app/modules/mfa-table/mfa-table.component.scss +++ b/console/src/app/modules/mfa-table/mfa-table.component.scss @@ -32,7 +32,7 @@ left: -2px; transform: translateX(-50%) translateY(-50%); cursor: pointer; - color: #f44336; + color: var(--warn); transition: all .2s ease; &[disabled] { diff --git a/console/src/app/modules/name-dialog/name-dialog.component.html b/console/src/app/modules/name-dialog/name-dialog.component.html new file mode 100644 index 0000000000..590eb7a4a8 --- /dev/null +++ b/console/src/app/modules/name-dialog/name-dialog.component.html @@ -0,0 +1,20 @@ +

+ {{data.titleKey | translate}} {{data?.number}} +

+

{{data.descKey | translate}}

+
+ + {{ data.labelKey | translate }} + + +
+
+ + + +
\ No newline at end of file diff --git a/console/src/app/modules/name-dialog/name-dialog.component.scss b/console/src/app/modules/name-dialog/name-dialog.component.scss new file mode 100644 index 0000000000..01bb81ffe6 --- /dev/null +++ b/console/src/app/modules/name-dialog/name-dialog.component.scss @@ -0,0 +1,26 @@ +h1 { + font-size: 1.5rem; + margin: 0; +} + +.desc { + font-size: 14px; + color: var(--grey); +} + +.formfield { + width: 100%; +} + +.action { + display: flex; + justify-content: flex-end; + + .ok-button { + margin-left: .5rem; + } + + button { + border-radius: .5rem; + } +} diff --git a/console/src/app/modules/name-dialog/name-dialog.component.spec.ts b/console/src/app/modules/name-dialog/name-dialog.component.spec.ts new file mode 100644 index 0000000000..f65ec4f120 --- /dev/null +++ b/console/src/app/modules/name-dialog/name-dialog.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; + +import { CodeDialogComponent } from './code-dialog.component'; + +describe('CodeDialogComponent', () => { + let component: CodeDialogComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [CodeDialogComponent], + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(CodeDialogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/console/src/app/modules/name-dialog/name-dialog.component.ts b/console/src/app/modules/name-dialog/name-dialog.component.ts new file mode 100644 index 0000000000..551c5133cc --- /dev/null +++ b/console/src/app/modules/name-dialog/name-dialog.component.ts @@ -0,0 +1,19 @@ +import { Component, Inject } from '@angular/core'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; + +@Component({ + selector: 'app-name-dialog', + templateUrl: './name-dialog.component.html', + styleUrls: ['./name-dialog.component.scss'], +}) +export class NameDialogComponent { + public name: string = ''; + constructor(public dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: any) { + this.name = data.name ?? ''; + } + + closeDialog(name: string = ''): void { + this.dialogRef.close(name); + } +} diff --git a/console/src/app/modules/name-dialog/name-dialog.module.ts b/console/src/app/modules/name-dialog/name-dialog.module.ts new file mode 100644 index 0000000000..fa24ad3add --- /dev/null +++ b/console/src/app/modules/name-dialog/name-dialog.module.ts @@ -0,0 +1,27 @@ +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { MatButtonModule } from '@angular/material/button'; +import { MatDialogModule } from '@angular/material/dialog'; +import { TranslateModule } from '@ngx-translate/core'; + +import { InputModule } from '../input/input.module'; +import { NameDialogComponent } from './name-dialog.component'; + +@NgModule({ + declarations: [ + NameDialogComponent, + ], + imports: [ + CommonModule, + MatDialogModule, + MatButtonModule, + TranslateModule, + InputModule, + FormsModule, + ], + exports: [ + NameDialogComponent, + ], +}) +export class NameDialogModule { } diff --git a/console/src/app/modules/password-complexity-view/password-complexity-view.component.scss b/console/src/app/modules/password-complexity-view/password-complexity-view.component.scss index e72abd1180..0531f30b51 100644 --- a/console/src/app/modules/password-complexity-view/password-complexity-view.component.scss +++ b/console/src/app/modules/password-complexity-view/password-complexity-view.component.scss @@ -47,7 +47,7 @@ } &.red { - color: #f44336; + color: var(--warn); } } } diff --git a/console/src/app/modules/policies/login-policy/login-policy.component.html b/console/src/app/modules/policies/login-policy/login-policy.component.html index 2dfcd1d56d..b83f9e6e6b 100644 --- a/console/src/app/modules/policies/login-policy/login-policy.component.html +++ b/console/src/app/modules/policies/login-policy/login-policy.component.html @@ -29,12 +29,9 @@ {{'POLICY.DATA.ALLOWUSERNAMEPASSWORD' | translate}} - - {{'FEATURES.NOTAVAILABLE' | translate: ({value: - 'login_policy.username_login'})}} - - + + + @@ -49,12 +46,10 @@ {{'POLICY.DATA.ALLOWREGISTER' | translate}} - - {{'FEATURES.NOTAVAILABLE' | translate: ({value: - 'login_policy.registration'})}} - - + + + + {{'POLICY.DATA.ALLOWREGISTER_DESC' | translate}} @@ -66,12 +61,11 @@ [(ngModel)]="loginData.allowExternalIdp"> {{'POLICY.DATA.ALLOWEXTERNALIDP' | translate}} - - {{'FEATURES.NOTAVAILABLE' | translate: ({value: - 'login_policy.idp'})}} - - + + + + + {{'POLICY.DATA.ALLOWEXTERNALIDP_DESC' | translate}} @@ -83,12 +77,11 @@ [(ngModel)]="loginData.forceMfa"> {{'POLICY.DATA.FORCEMFA' | translate}} - - {{'FEATURES.NOTAVAILABLE' | translate: ({value: - 'login_policy.factors'})}} - - + + + + + {{'POLICY.DATA.FORCEMFA_DESC' | translate}} @@ -101,12 +94,9 @@ {{'POLICY.DATA.HIDEPASSWORDRESET' | translate}} - - {{'FEATURES.NOTAVAILABLE' | translate: ({value: - 'login_policy.hide_password_reset'})}} - - + + + @@ -116,7 +106,6 @@
- {{'LOGINPOLICY.PASSWORDLESS' | translate}} - - {{'FEATURES.NOTAVAILABLE' | translate: ({value: - 'login_policy.passwordless'})}} - - + + + +
@@ -143,11 +130,11 @@

{{ 'MFA.LIST.MULTIFACTORTITLE' | translate }}

{{ 'MFA.LIST.MULTIFACTORDESCRIPTION' | translate }}

- - {{'FEATURES.NOTAVAILABLE' | translate: ({value: 'login_policy.factors'})}} - - + + + + + @@ -155,11 +142,11 @@

{{ 'MFA.LIST.SECONDFACTORTITLE' | translate }}

{{ 'MFA.LIST.SECONDFACTORDESCRIPTION' | translate }}

- - {{'FEATURES.NOTAVAILABLE' | translate: ({value: 'login_policy.factors'})}} - - + + + + + @@ -168,12 +155,9 @@

{{'LOGINPOLICY.IDPS' | translate}}

- - {{'FEATURES.NOTAVAILABLE' | translate: ({value: - 'login_policy.idp'})}} - - + + +
- - {{'FEATURES.NOTAVAILABLE' | translate: ({value: - 'custom_text.login'})}} + +
diff --git a/console/src/app/modules/policies/message-texts/message-texts.component.html b/console/src/app/modules/policies/message-texts/message-texts.component.html index a0bc1b2e3f..298b158eb7 100644 --- a/console/src/app/modules/policies/message-texts/message-texts.component.html +++ b/console/src/app/modules/policies/message-texts/message-texts.component.html @@ -19,12 +19,9 @@
- - {{'FEATURES.NOTAVAILABLE' | translate: ({value: - 'custom_text.message'})}} - + + +
{{'POLICY.DEFAULTLABEL' | translate}} + + + +
- @@ -20,11 +24,11 @@ {{'POLICY.DATA.MINLENGTH' | translate}}
- {{complexityData?.minLength}} -
@@ -33,14 +37,14 @@ {{'POLICY.DATA.HASNUMBER' | translate}} - +
{{'POLICY.DATA.HASSYMBOL' | translate}} - +
@@ -48,7 +52,7 @@ {{'POLICY.DATA.HASLOWERCASE' | translate}} + [(ngModel)]="complexityData.hasLowercase" [disabled]="serviceType == PolicyComponentServiceType.MGMT && (['password_complexity_policy'] | hasFeature | async) == false">
@@ -56,13 +60,13 @@ {{'POLICY.DATA.HASUPPERCASE' | translate}} + [(ngModel)]="complexityData.hasUppercase" [disabled]="serviceType == PolicyComponentServiceType.MGMT && (['password_complexity_policy'] | hasFeature | async) == false">
-
diff --git a/console/src/app/modules/policies/password-complexity-policy/password-complexity-policy.component.ts b/console/src/app/modules/policies/password-complexity-policy/password-complexity-policy.component.ts index e87b8343ab..d5dac47b7b 100644 --- a/console/src/app/modules/policies/password-complexity-policy/password-complexity-policy.component.ts +++ b/console/src/app/modules/policies/password-complexity-policy/password-complexity-policy.component.ts @@ -61,6 +61,7 @@ export class PasswordComplexityPolicyComponent implements OnDestroy { this.getData().then(data => { if (data.policy) { + console.log(data); this.complexityData = data.policy; this.loading = false; } diff --git a/console/src/app/modules/policies/password-complexity-policy/password-complexity-policy.module.ts b/console/src/app/modules/policies/password-complexity-policy/password-complexity-policy.module.ts index 015a9536be..297a8a6da0 100644 --- a/console/src/app/modules/policies/password-complexity-policy/password-complexity-policy.module.ts +++ b/console/src/app/modules/policies/password-complexity-policy/password-complexity-policy.module.ts @@ -7,9 +7,11 @@ 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 { InfoSectionModule } from '../../info-section/info-section.module'; import { PolicyGridModule } from '../../policy-grid/policy-grid.module'; @@ -29,6 +31,8 @@ import { PasswordComplexityPolicyComponent } from './password-complexity-policy. HasRoleModule, MatTooltipModule, TranslateModule, + HasFeatureModule, + HasFeaturePipeModule, DetailLayoutModule, MatProgressSpinnerModule, PolicyGridModule, diff --git a/console/src/app/modules/policies/password-lockout-policy/password-lockout-policy.component.html b/console/src/app/modules/policies/password-lockout-policy/password-lockout-policy.component.html index 76c95df885..08a6c943c3 100644 --- a/console/src/app/modules/policies/password-lockout-policy/password-lockout-policy.component.html +++ b/console/src/app/modules/policies/password-lockout-policy/password-lockout-policy.component.html @@ -1,9 +1,14 @@ + {{'POLICY.DEFAULTLABEL' | translate}} + + + + - @@ -14,11 +19,11 @@ {{'POLICY.DATA.MAXATTEMPTS' | translate}}
- {{lockoutData?.maxPasswordAttempts}} -
@@ -26,7 +31,7 @@
-
\ No newline at end of file diff --git a/console/src/app/modules/policies/password-lockout-policy/password-lockout-policy.component.ts b/console/src/app/modules/policies/password-lockout-policy/password-lockout-policy.component.ts index 455fa0b458..b2705ea8f1 100644 --- a/console/src/app/modules/policies/password-lockout-policy/password-lockout-policy.component.ts +++ b/console/src/app/modules/policies/password-lockout-policy/password-lockout-policy.component.ts @@ -23,7 +23,6 @@ export class PasswordLockoutPolicyComponent implements OnDestroy { @Input() public service!: ManagementService | AdminService; public serviceType: PolicyComponentServiceType = PolicyComponentServiceType.MGMT; - public lockoutForm!: FormGroup; public lockoutData!: LockoutPolicy.AsObject; private sub: Subscription = new Subscription(); diff --git a/console/src/app/modules/policies/password-lockout-policy/password-lockout-policy.module.ts b/console/src/app/modules/policies/password-lockout-policy/password-lockout-policy.module.ts index d72bd450ae..700a4e9494 100644 --- a/console/src/app/modules/policies/password-lockout-policy/password-lockout-policy.module.ts +++ b/console/src/app/modules/policies/password-lockout-policy/password-lockout-policy.module.ts @@ -9,6 +9,7 @@ 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 { InfoSectionModule } from '../../info-section/info-section.module'; import { PasswordLockoutPolicyRoutingModule } from './password-lockout-policy-routing.module'; @@ -28,6 +29,7 @@ import { PasswordLockoutPolicyComponent } from './password-lockout-policy.compon MatTooltipModule, TranslateModule, DetailLayoutModule, + HasFeaturePipeModule, InfoSectionModule, ], }) diff --git a/console/src/app/modules/policies/privacy-policy/privacy-policy.component.html b/console/src/app/modules/policies/privacy-policy/privacy-policy.component.html index add68cc30d..5d0a95a94a 100644 --- a/console/src/app/modules/policies/privacy-policy/privacy-policy.component.html +++ b/console/src/app/modules/policies/privacy-policy/privacy-policy.component.html @@ -4,11 +4,8 @@ {{'POLICY.DEFAULTLABEL' | translate}} - - {{'FEATURES.NOTAVAILABLE' | translate: ({value: - 'privacy_policy'})}} + +
diff --git a/console/src/app/modules/policies/private-labeling-policy/private-labeling-policy.component.html b/console/src/app/modules/policies/private-labeling-policy/private-labeling-policy.component.html index 6d2e3ed7fb..57262b7af4 100644 --- a/console/src/app/modules/policies/private-labeling-policy/private-labeling-policy.component.html +++ b/console/src/app/modules/policies/private-labeling-policy/private-labeling-policy.component.html @@ -11,6 +11,10 @@

{{'POLICY.PRIVATELABELING.PREVIEW_DESCRIPTION' | translate}}

{{'POLICY.DEFAULTLABEL' | translate}} + + + +
@@ -57,9 +61,10 @@ -

Your Logo will be used in the Login itself, while the icon is used for smaller UI elements like in the organisation switcher in console

+

{{'POLICY.PRIVATELABELING.USEOFLOGO' | translate}}

{{'POLICY.PRIVATELABELING.MAXSIZE' | translate}} + {{'POLICY.PRIVATELABELING.EMAILNOSVG' | translate}}
@@ -242,20 +247,21 @@ - {{'FEATURES.NOTAVAILABLE' | translate: ({value: - 'label_policy.private_label'})}} + + - {{'POLICY.DATA.HIDELOGINNAMESUFFIX' | translate}} - {{'FEATURES.NOTAVAILABLE' | translate: ({value: - 'label_policy.watermark'})}} + + -
diff --git a/console/src/app/modules/refresh-table/refresh-table.component.scss b/console/src/app/modules/refresh-table/refresh-table.component.scss index 3724c9aa63..a7ccd70daa 100644 --- a/console/src/app/modules/refresh-table/refresh-table.component.scss +++ b/console/src/app/modules/refresh-table/refresh-table.component.scss @@ -40,5 +40,9 @@ .icon-button { margin-right: .5rem; + + .icon { + font-size: 1.2rem; + } } } diff --git a/console/src/app/modules/refresh-table/refresh-table.component.ts b/console/src/app/modules/refresh-table/refresh-table.component.ts index ddbb32ec35..532ed28b3d 100644 --- a/console/src/app/modules/refresh-table/refresh-table.component.ts +++ b/console/src/app/modules/refresh-table/refresh-table.component.ts @@ -5,56 +5,57 @@ import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb'; import { RefreshService } from 'src/app/services/refresh.service'; const rotate = animation([ - animate( - '{{time}} cubic-bezier(0.785, 0.135, 0.15, 0.86)', - keyframes([ - style({ - transform: 'rotate(0deg)', - }), - style({ - transform: 'rotate(360deg)', - }), - ]), - ), + animate( + '{{time}} cubic-bezier(0.785, 0.135, 0.15, 0.86)', + keyframes([ + style({ + transform: 'rotate(0deg)', + }), + style({ + transform: 'rotate(360deg)', + }), + ]), + ), ]); @Component({ - selector: 'app-refresh-table', - templateUrl: './refresh-table.component.html', - styleUrls: ['./refresh-table.component.scss'], - animations: [ - trigger('rotate', [ - transition('* => *', [useAnimation(rotate, { params: { time: '1s' } })]), - ]), - ], + selector: 'app-refresh-table', + templateUrl: './refresh-table.component.html', + styleUrls: ['./refresh-table.component.scss'], + animations: [ + trigger('rotate', [ + transition('* => *', [useAnimation(rotate, { params: { time: '1s' } })]), + ]), + ], }) export class RefreshTableComponent implements OnInit { - @Input() public selection: SelectionModel = new SelectionModel(true, []); - @Input() public timestamp!: Timestamp.AsObject; - @Input() public dataSize: number = 0; - @Input() public emitRefreshAfterTimeoutInMs: number = 0; - @Input() public loading: boolean = false; - @Input() public emitRefreshOnPreviousRoutes: string[] = []; - @Output() public refreshed: EventEmitter = new EventEmitter(); + @Input() public selection: SelectionModel = new SelectionModel(true, []); + @Input() public timestamp!: Timestamp.AsObject; + @Input() public dataSize: number = 0; + @Input() public emitRefreshAfterTimeoutInMs: number = 0; + @Input() public loading: boolean = false; + @Input() public emitRefreshOnPreviousRoutes: string[] = []; + @Output() public refreshed: EventEmitter = new EventEmitter(); + @Input() public hideRefresh: boolean = false; - constructor(private refreshService: RefreshService) { } + constructor(private refreshService: RefreshService) { } - ngOnInit(): void { - if (this.emitRefreshAfterTimeoutInMs) { - setTimeout(() => { - this.emitRefresh(); - }, this.emitRefreshAfterTimeoutInMs); - } - - if (this.emitRefreshOnPreviousRoutes.length && this.refreshService.previousUrls - .some(url => this.emitRefreshOnPreviousRoutes.includes(url))) { - setTimeout(() => { - this.emitRefresh(); - }, 1000); - } + ngOnInit(): void { + if (this.emitRefreshAfterTimeoutInMs) { + setTimeout(() => { + this.emitRefresh(); + }, this.emitRefreshAfterTimeoutInMs); } - emitRefresh(): void { - this.selection.clear(); - return this.refreshed.emit(); + if (this.emitRefreshOnPreviousRoutes.length && this.refreshService.previousUrls + .some(url => this.emitRefreshOnPreviousRoutes.includes(url))) { + setTimeout(() => { + this.emitRefresh(); + }, 1000); } + } + + emitRefresh(): void { + this.selection.clear(); + return this.refreshed.emit(); + } } diff --git a/console/src/app/pages/grants/grants.component.scss b/console/src/app/pages/grants/grants.component.scss index 9fb7ff0c3f..4b6cd08aae 100644 --- a/console/src/app/pages/grants/grants.component.scss +++ b/console/src/app/pages/grants/grants.component.scss @@ -1,8 +1,9 @@ h1 { - margin-top: 0; + margin: 0; } .desc { color: var(--grey); margin-bottom: 2rem; + font-size: 14px; } diff --git a/console/src/app/pages/orgs/org-detail/org-detail.component.html b/console/src/app/pages/orgs/org-detail/org-detail.component.html index 1c5f0f85a5..44b1872caa 100644 --- a/console/src/app/pages/orgs/org-detail/org-detail.component.html +++ b/console/src/app/pages/orgs/org-detail/org-detail.component.html @@ -1,13 +1,13 @@ -
+

{{org?.name}}

{{'ORG_DETAIL.DESCRIPTION' | translate}}

-
@@ -31,8 +31,8 @@

{{'ORG.PAGES.ORGDOMAIN.VERIFICATION' | translate}}

- - {{'ORG.PAGES.CUSTOMDOMAINFEATUREMISSING' | translate}} + +
- - - + + + + + + - - - - - - - - - - - + + +

{{ 'APP.PAGES.DESCRIPTION' | translate }}

@@ -50,104 +47,68 @@ {{errorMessage}} -
-
- - {{ 'APP.NAME' | translate }} - - -
-
- -
-
- {{'APP.OIDC.INFO.CLIENTID' | translate}} -
- {{this.app.oidcConfig?.clientId}} - -
-
-
- {{'APP.API.INFO.CLIENTID' | translate}} -
- {{this.app.apiConfig?.clientId}} - -
-
- -
- {{environmentV.key}} -
- {{environmentV.value}} - -
-
-
-
-
- -
    -
  • - {{problem.localizedMessage}}
  • -
-
+ *ngIf="app?.oidcConfig?.complianceProblemsList && app.oidcConfig?.complianceProblemsList?.length"> + +
    +
  • + {{problem.localizedMessage}}
  • +
+
-
-

{{'APP.OIDC.REDIRECTSECTIONTITLE' | translate}}

+ - - {{ 'APP.OIDC.DEVMODE' | translate }} - - - - {{'APP.OIDC.REDIRECTDESCRIPTIONNATIVE' | translate}} - - - + - - - - - - -
- -
+ + + +
- \ No newline at end of file + + \ No newline at end of file diff --git a/console/src/app/pages/users/user-detail/external-idps/external-idps.component.scss b/console/src/app/pages/users/user-detail/external-idps/external-idps.component.scss index 818d7f1c87..2dbdd2b14f 100644 --- a/console/src/app/pages/users/user-detail/external-idps/external-idps.component.scss +++ b/console/src/app/pages/users/user-detail/external-idps/external-idps.component.scss @@ -1,3 +1,8 @@ +.icon-button { + .icon { + font-size: 1.2rem; + } +} .table-wrapper { overflow: auto; diff --git a/console/src/app/pages/users/user-detail/metadata-dialog/metadata-dialog.component.html b/console/src/app/pages/users/user-detail/metadata-dialog/metadata-dialog.component.html new file mode 100644 index 0000000000..a7c3634322 --- /dev/null +++ b/console/src/app/pages/users/user-detail/metadata-dialog/metadata-dialog.component.html @@ -0,0 +1,40 @@ +
+

{{'USER.METADATA.TITLE' | translate}}

+ +

{{ts | timestampToDate | localizedDate: 'dd. MMM, HH:mm' }}

+ + +
+

{{'USER.METADATA.DESCRIPTION' | translate}}

+
+
+
+ + {{ 'USER.METADATA.KEY' | translate }} + + + + {{ 'USER.METADATA.VALUE' | translate }} + + + + + +
+
+ +
+
+ +
\ No newline at end of file diff --git a/console/src/app/pages/users/user-detail/metadata-dialog/metadata-dialog.component.scss b/console/src/app/pages/users/user-detail/metadata-dialog/metadata-dialog.component.scss new file mode 100644 index 0000000000..ac5a6377ed --- /dev/null +++ b/console/src/app/pages/users/user-detail/metadata-dialog/metadata-dialog.component.scss @@ -0,0 +1,58 @@ +.title-row { + display: flex; + align-items: center; + + .title { + font-size: 1.5rem; + margin: 0; + } + + .fill-space { + flex: 1; + } + + .ts { + margin: 0; + font-size: 14px; + color: var(--grey); + } + + .icon-button { + .icon { + font-size: 1.2rem; + } + } +} + +.content { + display: flex; + align-items: center; + margin: 0 -.5rem; + + .formfield { + flex: 1; + margin: 0 .5rem; + + @media only screen and (max-width: 450px) { + flex-basis: 100%; + } + } + + .rm-button, + .set-button { + margin-top: 14px; + } +} + +.action { + display: flex; + justify-content: flex-end; + + .ok-button { + margin-left: .5rem; + } + + button { + border-radius: .5rem; + } +} diff --git a/console/src/app/pages/users/user-detail/metadata-dialog/metadata-dialog.component.spec.ts b/console/src/app/pages/users/user-detail/metadata-dialog/metadata-dialog.component.spec.ts new file mode 100644 index 0000000000..483b8ae75b --- /dev/null +++ b/console/src/app/pages/users/user-detail/metadata-dialog/metadata-dialog.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; + +import { MetadataDialogComponent } from './metadata-dialog.component'; + +describe('MetadataDialogComponent', () => { + let component: MetadataDialogComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [MetadataDialogComponent], + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(MetadataDialogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/console/src/app/pages/users/user-detail/metadata-dialog/metadata-dialog.component.ts b/console/src/app/pages/users/user-detail/metadata-dialog/metadata-dialog.component.ts new file mode 100644 index 0000000000..fbab147a3c --- /dev/null +++ b/console/src/app/pages/users/user-detail/metadata-dialog/metadata-dialog.component.ts @@ -0,0 +1,116 @@ +import { Component, Inject } from '@angular/core'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb'; +import { Metadata } from 'src/app/proto/generated/zitadel/metadata_pb'; +import { ManagementService } from 'src/app/services/mgmt.service'; +import { ToastService } from 'src/app/services/toast.service'; + + +@Component({ + selector: 'app-metadata-dialog', + templateUrl: './metadata-dialog.component.html', + styleUrls: ['./metadata-dialog.component.scss'], +}) +export class MetadataDialogComponent { + public metadata: Partial[] = []; + public injData: any = {}; + public loading: boolean = true; + public ts!: Timestamp.AsObject | undefined; + + constructor( + private service: ManagementService, + private toast: ToastService, + public dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: any) { + this.injData = data; + this.load(); + } + + public load(): void { + this.loadMetadata().then(() => { + this.loading = false; + if (this.metadata.length === 0) { + this.addEntry(); + } + }).catch(error => { + this.loading = false; + this.toast.showError(error); + if (this.metadata.length === 0) { + this.addEntry(); + } + }); + } + + public loadMetadata(): Promise { + this.loading = true; + if (this.injData.userId) { + return this.service.listUserMetadata(this.injData.userId).then(resp => { + this.metadata = resp.resultList.map(md => { + return { + key: md.key, + value: atob(md.value as string), + }; + }); + this.ts = resp.details?.viewTimestamp; + }); + } else { + return Promise.reject(); + } + } + + public addEntry(): void { + const newGroup = { + key: '', + value: '', + }; + + this.metadata.push(newGroup); + } + + public removeEntry(index: number): void { + const key = this.metadata[index].key; + if (key) { + this.removeMetadata(key).then(() => { + this.metadata.splice(index, 1); + if (this.metadata.length === 0) { + this.addEntry(); + } + }); + } else { + this.metadata.splice(index, 1); + } + } + + public saveElement(index: number): void { + const metadataElement = this.metadata[index]; + + if (metadataElement.key && metadataElement.value) { + this.setMetadata(metadataElement.key, metadataElement.value as string); + } + } + + public setMetadata(key: string, value: string): void { + console.log(key, value, this.injData.userId); + if (key && value) { + this.service.setUserMetadata(key, btoa(value), this.injData.userId) + .then(() => { + this.toast.showInfo('USER.METADATA.SETSUCCESS', true); + }).catch(error => { + this.toast.showError(error); + }); + } + } + + public removeMetadata(key: string): Promise { + return this.service.removeUserMetadata(key, this.injData.userId) + .then((resp) => { + this.toast.showInfo('USER.METADATA.REMOVESUCCESS', true); + }).catch(error => { + this.toast.showError(error); + }); + } + + closeDialog(): void { + this.dialogRef.close(); + } +} diff --git a/console/src/app/pages/users/user-detail/metadata/metadata.component.html b/console/src/app/pages/users/user-detail/metadata/metadata.component.html new file mode 100644 index 0000000000..85b2b465e3 --- /dev/null +++ b/console/src/app/pages/users/user-detail/metadata/metadata.component.html @@ -0,0 +1,21 @@ + diff --git a/console/src/app/pages/users/user-detail/metadata/metadata.component.scss b/console/src/app/pages/users/user-detail/metadata/metadata.component.scss new file mode 100644 index 0000000000..595d6451d9 --- /dev/null +++ b/console/src/app/pages/users/user-detail/metadata/metadata.component.scss @@ -0,0 +1,72 @@ +.metadata-details { + padding-bottom: 1rem; + + .actions { + display: flex; + align-items: center; + + .edit { + font-size: 14px; + } + } + + .meta-row { + display: flex; + margin-bottom: .5rem; + align-items: center; + + .first { + flex: 1; + font-size: 13px; + margin-right: .5rem; + } + + .fill-space { + flex: 1; + } + + .second { + font-size: 13px; + } + } + + .metadata-set { + display: flex; + flex-direction: column; + margin-bottom: .5rem; + + .first { + font-size: 14px; + color: var(--grey); + } + } +} + +.border-t { + border-top: 1px solid #81868a40; +} + +.edit { + font-size: 14px; + cursor: pointer; +} + +.empty-desc { + margin: 0; + font-size: 14px; + color: var(--grey); +} + +.ts { + font-size: 14px; + color: var(--grey); + margin-top: 0; +} + +.refresh-btn { + float: left; + + .icon { + font-size: 1.2rem; + } +} diff --git a/console/src/app/pages/users/user-detail/metadata/metadata.component.spec.ts b/console/src/app/pages/users/user-detail/metadata/metadata.component.spec.ts new file mode 100644 index 0000000000..d3e10ecb7c --- /dev/null +++ b/console/src/app/pages/users/user-detail/metadata/metadata.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MetadataComponent } from './metadata.component'; + +describe('MetadataComponent', () => { + let component: MetadataComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [MetadataComponent], + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(MetadataComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/console/src/app/pages/users/user-detail/metadata/metadata.component.ts b/console/src/app/pages/users/user-detail/metadata/metadata.component.ts new file mode 100644 index 0000000000..e0a716f42b --- /dev/null +++ b/console/src/app/pages/users/user-detail/metadata/metadata.component.ts @@ -0,0 +1,53 @@ +import { Component, Input, OnInit } from '@angular/core'; +import { MatDialog } from '@angular/material/dialog'; +import { Metadata } from 'src/app/proto/generated/zitadel/metadata_pb'; +import { ManagementService } from 'src/app/services/mgmt.service'; +import { ToastService } from 'src/app/services/toast.service'; + +import { MetadataDialogComponent } from '../metadata-dialog/metadata-dialog.component'; + +@Component({ + selector: 'cnsl-metadata', + templateUrl: './metadata.component.html', + styleUrls: ['./metadata.component.scss'], +}) +export class MetadataComponent implements OnInit { + @Input() userId: string = ''; + public metadata: Metadata.AsObject[] = []; + public loading: boolean = false; + + constructor(private dialog: MatDialog, private service: ManagementService, private toast: ToastService, + ) { } + + ngOnInit(): void { + this.loadMetadata(); + } + + public editMetadata(): void { + const dialogRef = this.dialog.open(MetadataDialogComponent, { + data: { + userId: this.userId, + }, + }); + + dialogRef.afterClosed().subscribe(() => { + this.loadMetadata(); + }); + } + + public loadMetadata(): Promise { + this.loading = true; + return (this.service as ManagementService).listUserMetadata(this.userId).then(resp => { + this.loading = false; + this.metadata = resp.resultList.map(md => { + return { + key: md.key, + value: atob(md.value as string), + }; + }); + }).catch((error) => { + this.loading = false; + this.toast.showError(error); + }); + } +} diff --git a/console/src/app/pages/users/user-detail/user-detail.module.ts b/console/src/app/pages/users/user-detail/user-detail.module.ts index 715ad845ec..80ccfb7e6a 100644 --- a/console/src/app/pages/users/user-detail/user-detail.module.ts +++ b/console/src/app/pages/users/user-detail/user-detail.module.ts @@ -5,9 +5,11 @@ import { MatButtonModule } from '@angular/material/button'; import { MatCheckboxModule } from '@angular/material/checkbox'; import { MatDialogModule } from '@angular/material/dialog'; import { MatIconModule } from '@angular/material/icon'; +import { MatMenuModule } from '@angular/material/menu'; import { MatProgressBarModule } from '@angular/material/progress-bar'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatTableModule } from '@angular/material/table'; +import { MatTabsModule } from '@angular/material/tabs'; import { MatTooltipModule } from '@angular/material/tooltip'; import { TranslateModule } from '@ngx-translate/core'; import { QRCodeModule } from 'angularx-qrcode'; @@ -31,6 +33,8 @@ 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 { @@ -48,6 +52,8 @@ import { DetailFormMachineModule } from './detail-form-machine/detail-form-machi import { DetailFormModule } from './detail-form/detail-form.module'; import { ExternalIdpsComponent } from './external-idps/external-idps.component'; import { MembershipsComponent } from './memberships/memberships.component'; +import { MetadataDialogComponent } from './metadata-dialog/metadata-dialog.component'; +import { MetadataComponent } from './metadata/metadata.component'; import { PasswordComponent } from './password/password.component'; import { UserDetailRoutingModule } from './user-detail-routing.module'; import { PasswordlessComponent } from './user-detail/passwordless/passwordless.component'; @@ -73,11 +79,14 @@ import { UserMfaComponent } from './user-detail/user-mfa/user-mfa.component'; DialogU2FComponent, DialogPasswordlessComponent, AuthFactorDialogComponent, + MetadataDialogComponent, + MetadataComponent, ], imports: [ UserDetailRoutingModule, ChangesModule, CommonModule, + MatTabsModule, FormsModule, ReactiveFormsModule, DetailFormModule, @@ -94,11 +103,14 @@ import { UserMfaComponent } from './user-detail/user-mfa/user-mfa.component'; CardModule, MatProgressSpinnerModule, MatProgressBarModule, + HasFeatureModule, MatTooltipModule, HasRoleModule, TranslateModule, MatTableModule, + InfoRowModule, PaginatorModule, + MatMenuModule, SharedModule, RefreshTableModule, CopyToClipboardModule, diff --git a/console/src/app/pages/users/user-detail/user-detail/passwordless/passwordless.component.html b/console/src/app/pages/users/user-detail/user-detail/passwordless/passwordless.component.html index 899d9d1e18..47dcd292c0 100644 --- a/console/src/app/pages/users/user-detail/user-detail/passwordless/passwordless.component.html +++ b/console/src/app/pages/users/user-detail/user-detail/passwordless/passwordless.component.html @@ -1,6 +1,9 @@ - + refresh + + diff --git a/console/src/app/pages/users/user-detail/user-detail/passwordless/passwordless.component.scss b/console/src/app/pages/users/user-detail/user-detail/passwordless/passwordless.component.scss index 5cb25ad590..89a49f91fc 100644 --- a/console/src/app/pages/users/user-detail/user-detail/passwordless/passwordless.component.scss +++ b/console/src/app/pages/users/user-detail/user-detail/passwordless/passwordless.component.scss @@ -1,3 +1,9 @@ +.icon-button { + .icon { + font-size: 1.2rem; + } +} + .centered { display: flex; align-items: center; diff --git a/console/src/app/pages/users/user-detail/user-detail/user-detail.component.html b/console/src/app/pages/users/user-detail/user-detail/user-detail.component.html index 9ab9f17526..e6468e50ed 100644 --- a/console/src/app/pages/users/user-detail/user-detail/user-detail.component.html +++ b/console/src/app/pages/users/user-detail/user-detail/user-detail.component.html @@ -4,28 +4,34 @@ arrow_back -

{{user.human ? user.human?.profile?.displayName : user.machine?.name}}

- - - - +
+

{{user.human ? user.human?.profile?.displayName : user.machine?.name}}

+

{{user?.preferredLoginName}}

+
- - + + - - - + + + + + + @@ -34,19 +40,7 @@ {{'USER.PAGES.LOCKEDDESCRIPTION' | translate}} {{ 'USER.PAGES.NOUSER' | translate }} - - - + @@ -57,8 +51,8 @@ - - - - - + + + + + +
-
-
- {{'RESOURCEID' | translate}}: - {{ user.id }} -
-
- {{'USER.PREFERRED_LOGINNAME' | translate}} - {{user.preferredLoginName}} -
-
- {{'ORG.PAGES.STATE' | translate}} - {{'USER.DATA.STATE'+user.state - | translate}} -
+
+
+ {{'RESOURCEID' | translate}}: + {{ user.id }}
+
+ {{'USER.PREFERRED_LOGINNAME' | translate}} + {{user.preferredLoginName}} +
+
+ {{'USER.PAGES.STATE' | translate}} + {{'USER.DATA.STATE'+user.state + | translate}} +
+
- - - - - - + + +
+ + + +
+
+ + + + +
\ No newline at end of file diff --git a/console/src/app/pages/users/user-detail/user-detail/user-detail.component.scss b/console/src/app/pages/users/user-detail/user-detail/user-detail.component.scss index 88632f79bd..22daeb0bc0 100644 --- a/console/src/app/pages/users/user-detail/user-detail/user-detail.component.scss +++ b/console/src/app/pages/users/user-detail/user-detail/user-detail.component.scss @@ -1,29 +1,43 @@ .head { display: flex; - align-items: center; + align-items: flex-start; flex-wrap: wrap; - padding-bottom: .5rem; + padding-bottom: 2rem; a { display: block; margin-right: 2rem; } - h1 { - margin: 0; - margin-right: 1rem; + .head-row { + display: flex; + flex-direction: column; + + h1 { + margin: 0; + margin-right: 1rem; + } + + p { + margin: .5rem 0; + font-size: 14px; + color: var(--grey); + } } .fill-space { flex: 1; } - .unlock-button { - margin-left: .5rem; - } + .actions-trigger { + margin-top: .25rem; + display: flex; + align-items: center; - .state-button { - margin-left: .5rem; + .icon { + margin-left: .5rem; + margin-right: -.5rem; + } } } @@ -32,6 +46,12 @@ margin: 1rem 0; } +.icon-button { + .icon { + font-size: 1.2rem; + } +} + .img-phone-email { width: 300px; } @@ -42,3 +62,7 @@ min-height: 0; } } + +.side-padding { + padding-top: 1rem; +} diff --git a/console/src/app/pages/users/user-detail/user-detail/user-detail.component.ts b/console/src/app/pages/users/user-detail/user-detail/user-detail.component.ts index 78f698e842..48198197d3 100644 --- a/console/src/app/pages/users/user-detail/user-detail/user-detail.component.ts +++ b/console/src/app/pages/users/user-detail/user-detail/user-detail.component.ts @@ -8,6 +8,7 @@ import { ChangeType } from 'src/app/modules/changes/changes.component'; import { UserGrantContext } from 'src/app/modules/user-grants/user-grants-datasource'; import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component'; import { SendHumanResetPasswordNotificationRequest, UnlockUserRequest } from 'src/app/proto/generated/zitadel/management_pb'; +import { Metadata } from 'src/app/proto/generated/zitadel/metadata_pb'; import { Email, Gender, Machine, Phone, Profile, User, UserState } from 'src/app/proto/generated/zitadel/user_pb'; import { ManagementService } from 'src/app/services/mgmt.service'; import { ToastService } from 'src/app/services/toast.service'; @@ -22,6 +23,7 @@ import { ResendEmailDialogComponent } from '../auth-user-detail/resend-email-dia }) export class UserDetailComponent implements OnInit { public user!: User.AsObject; + public metadata: Metadata.AsObject[] = []; public genders: Gender[] = [Gender.GENDER_MALE, Gender.GENDER_FEMALE, Gender.GENDER_DIVERSE]; public languages: string[] = ['de', 'en']; @@ -56,6 +58,14 @@ export class UserDetailComponent implements OnInit { }).catch(err => { console.error(err); }); + + this.mgmtUserService.listUserMetadata(id, 0, 100, []).then(resp => { + if (resp.resultList) { + this.metadata = resp.resultList; + } + }).catch(err => { + console.error(err); + }); }); } diff --git a/console/src/app/pages/users/user-detail/user-detail/user-mfa/user-mfa.component.html b/console/src/app/pages/users/user-detail/user-detail/user-mfa/user-mfa.component.html index c06b9553b9..026a631786 100644 --- a/console/src/app/pages/users/user-detail/user-detail/user-mfa/user-mfa.component.html +++ b/console/src/app/pages/users/user-detail/user-detail/user-mfa/user-mfa.component.html @@ -1,5 +1,8 @@ - + +
diff --git a/console/src/app/pages/users/user-detail/user-detail/user-mfa/user-mfa.component.scss b/console/src/app/pages/users/user-detail/user-detail/user-mfa/user-mfa.component.scss index 415be21263..85a557481c 100644 --- a/console/src/app/pages/users/user-detail/user-detail/user-mfa/user-mfa.component.scss +++ b/console/src/app/pages/users/user-detail/user-detail/user-mfa/user-mfa.component.scss @@ -1,3 +1,8 @@ +.icon-button { + .icon { + font-size: 1.2rem; + } +} .col { display: flex; diff --git a/console/src/app/pages/users/user-list/user-list.component.scss b/console/src/app/pages/users/user-list/user-list.component.scss index 7abeeb71a1..323eba0150 100644 --- a/console/src/app/pages/users/user-list/user-list.component.scss +++ b/console/src/app/pages/users/user-list/user-list.component.scss @@ -1,8 +1,9 @@ h1 { - margin-top: 0; + margin: 0; } .sub { color: var(--grey); margin-bottom: 2rem; + font-size: 14px; } diff --git a/console/src/app/services/mgmt.service.ts b/console/src/app/services/mgmt.service.ts index 4305f05527..db65123d66 100644 --- a/console/src/app/services/mgmt.service.ts +++ b/console/src/app/services/mgmt.service.ts @@ -64,6 +64,8 @@ import { BulkAddProjectRolesResponse, BulkRemoveUserGrantRequest, BulkRemoveUserGrantResponse, + BulkSetUserMetadataRequest, + BulkSetUserMetadataResponse, DeactivateAppRequest, DeactivateAppResponse, DeactivateOrgIDPRequest, @@ -160,6 +162,8 @@ import { GetUserByLoginNameGlobalResponse, GetUserGrantByIDRequest, GetUserGrantByIDResponse, + GetUserMetadataRequest, + GetUserMetadataResponse, IDPQuery, ListAppChangesRequest, ListAppChangesResponse, @@ -214,6 +218,8 @@ import { ListUserGrantResponse, ListUserMembershipsRequest, ListUserMembershipsResponse, + ListUserMetadataRequest, + ListUserMetadataResponse, ListUsersRequest, ListUsersResponse, ReactivateAppRequest, @@ -282,6 +288,8 @@ import { RemoveSecondFactorFromLoginPolicyResponse, RemoveUserGrantRequest, RemoveUserGrantResponse, + RemoveUserMetadataRequest, + RemoveUserMetadataResponse, RemoveUserRequest, RemoveUserResponse, ResendHumanEmailVerificationRequest, @@ -334,6 +342,8 @@ import { SetHumanInitialPasswordRequest, SetPrimaryOrgDomainRequest, SetPrimaryOrgDomainResponse, + SetUserMetadataRequest, + SetUserMetadataResponse, UnlockUserRequest, UnlockUserResponse, UpdateAPIAppConfigRequest, @@ -384,6 +394,7 @@ import { ValidateOrgDomainResponse, } from '../proto/generated/zitadel/management_pb'; import { SearchQuery } from '../proto/generated/zitadel/member_pb'; +import { MetadataQuery } from '../proto/generated/zitadel/metadata_pb'; import { ListQuery } from '../proto/generated/zitadel/object_pb'; import { DomainSearchQuery, DomainValidationType } from '../proto/generated/zitadel/org_pb'; import { PasswordComplexityPolicy } from '../proto/generated/zitadel/policy_pb'; @@ -1175,6 +1186,7 @@ export class ManagementService { ): Promise { const req = new UpdateCustomLockoutPolicyRequest(); req.setMaxPasswordAttempts(maxAttempts); + return this.grpcService.mgmt.updateCustomLockoutPolicy(req, null).then(resp => resp.toObject()); } @@ -1196,6 +1208,54 @@ export class ManagementService { return this.grpcService.mgmt.getUserByID(req, null).then(resp => resp.toObject()); } + public listUserMetadata(userId: string, offset?: number, limit?: number, queryList?: MetadataQuery[]): + Promise { + const req = new ListUserMetadataRequest(); + + req.setId(userId); + const metadata = new ListQuery(); + if (offset) { + metadata.setOffset(offset); + } + if (limit) { + metadata.setLimit(limit); + } + if (queryList) { + req.setQueriesList(queryList); + } + return this.grpcService.mgmt.listUserMetadata(req, null).then(resp => resp.toObject()); + } + + public getUserMetadata(userId: string, key: string): Promise { + const req = new GetUserMetadataRequest(); + req.setId(userId); + req.setKey(key); + return this.grpcService.mgmt.getUserMetadata(req, null).then(resp => resp.toObject()); + } + + public setUserMetadata(key: string, value: string, userId: string): Promise { + const req = new SetUserMetadataRequest(); + req.setKey(key); + req.setValue(value); + req.setId(userId); + return this.grpcService.mgmt.setUserMetadata(req, null).then(resp => resp.toObject()); + } + + public bulkSetUserMetadata(list: BulkSetUserMetadataRequest.Metadata[], userId: string): + Promise { + const req = new BulkSetUserMetadataRequest(); + req.setMetadataList(list); + req.setId(userId); + return this.grpcService.mgmt.bulkSetUserMetadata(req, null).then(resp => resp.toObject()); + } + + public removeUserMetadata(key: string, userId: string): Promise { + const req = new RemoveUserMetadataRequest(); + req.setKey(key); + req.setId(userId); + return this.grpcService.mgmt.removeUserMetadata(req, null).then(resp => resp.toObject()); + } + public removeUser(id: string): Promise { const req = new RemoveUserRequest(); req.setId(id); diff --git a/console/src/assets/i18n/de.json b/console/src/assets/i18n/de.json index 30b95e02d6..4509bc565f 100644 --- a/console/src/assets/i18n/de.json +++ b/console/src/assets/i18n/de.json @@ -106,7 +106,11 @@ } }, "ACTIONS": { + "ACTIONS":"Aktionen", + "RENAME":"Umbenennen", "SET":"Übernehmen", + "COPY":"In die Zwischenablage kopieren", + "COPIED":"In die Zwischenablage kopiert.", "RESETDEFAULT":"Auf Standard zurücksetzen", "RESETTO":"Zurücksetzen auf: ", "RESETCURRENT":"Auf aktuellen Wert zurücksetzen", @@ -141,7 +145,8 @@ "SEND": "Senden", "NEWVALUE": "Neuer Wert", "RESTORE":"Wiederherstellen", - "CONTINUEWITHOUTSAVE":"Ohne speichern fortfahren" + "CONTINUEWITHOUTSAVE":"Ohne speichern fortfahren", + "GOTOFEATURES":"Zu den Features" }, "RESOURCEID": "Ressourcen-ID", "TABLE": { @@ -168,16 +173,19 @@ "MY": "Meine Informationen", "LOGINNAMES": "Login-Namen", "LOGINNAMESDESC": "Mit diesen Namen kannst Du Dich anmelden.", - "COPY": "In die Zwischenablage kopieren", - "COPIED": "In die Zwischenablage kopiert.", "NOUSER": "Kein Benutzer", "REACTIVATE": "Reaktivieren", "DEACTIVATE": "Deaktivieren", "FILTER": "Filter", + "STATE":"Status", "DELETE": "Benutzer löschen", "UNLOCK": "Benutzer entsperren", "LOCKEDDESCRIPTION":"Dieser Benutzer wurde aufgrund der Überschreitung der maximalen Anmeldeversuche gesperrt und muss zur erneuten Verwendung entsperrt werden." }, + "DETAILS": { + "DATECREATED":"Erstellt", + "DATECHANGED":"Geändert" + }, "DIALOG": { "DELETE_TITLE": "User löschen", "DELETE_DESCRIPTION": "Sie sind im Begriff einen Benutzer endgültig zu löschen. Wollen Sie dies wirklich tun?" @@ -243,6 +251,17 @@ "NEW":"Hinzufügen" } }, + "METADATA": { + "TITLE":"Metadata", + "DESCRIPTION":"", + "KEY":"Schlüssel", + "VALUE":"Wert", + "ADD":"Neues Element", + "SAVE":"Speichern", + "EMPTY":"Keine Metadaten", + "SETSUCCESS":"Element erfolgreich gespeichert", + "REMOVESUCCESS":"Element erfolgreich gelöscht" + }, "MFA": { "TABLETYPE": "Typ", "TABLESTATE": "Status", @@ -287,8 +306,8 @@ "USERDISPLAYNAME": "Externer Name", "EXTERNALUSERID": "Externe Benutzer ID", "DIALOG": { - "REMOVE_TITLE": "Idp entfernen", - "REMOVE_DESCRIPTION": "Sie sind im Begriff einen Identity Provider zu entfernen. Wollen Sie dies wirklich tun?" + "DELETE_TITLE": "Idp entfernen", + "DELETE_DESCRIPTION": "Sie sind im Begriff einen Identity Provider zu entfernen. Wollen Sie dies wirklich tun?" } }, "CREATE": { @@ -562,7 +581,6 @@ "ORGDETAIL_TITLE": "Gebe den Namen und die Domain für die neue Organisation ein.", "ORGDETAIL_TITLE_WITHOUT_DOMAIN": "Geben Sie den Namen der neuen Organisation ein.", "ORGDETAILUSER_TITLE": "Organisationsbesitzer hinzufügen", - "CUSTOMDOMAINFEATUREMISSING":"Das Feature custom-domain ist auf Ihrer Organisation nicht freigeschaltet!", "ORGDOMAIN": { "TITLE": "Verifikation der Domain der Organisation", "VERIFICATION": "Überprüfe den Besitz Deiner Domain, indem Du eine Bestätigungsdatei herunterlädst und unter der angegebenen URL speicherst, oder indem Du sie mit einem DNS-Eintrag verifizierst.", @@ -623,6 +641,8 @@ "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", @@ -639,20 +659,31 @@ "QUESTIONS": "Bei Fragen kontaktieren Sie unseren Support per Mail an ", "BTN": "Preisstufe ändern" }, + "HEADERS": { + "LOGINPOLICY":"Login Richtlinie", + "PASSWORD":"Passwort", + "LABELPOLICY":"Privatelabelling", + "DOMAIN":"Organisations Domänen", + "TEXTSANDLINKS":"Texte und Links", + "METADATA":"Metadata" + }, "DATA": { "AUDITLOGRETENTION": "Audit Log Retention", - "LOGINPOLICYUSERNAMELOGIN": "Login Richtlinie: Login mit Username erlauben - benutzerdefiniert", - "LOGINPOLICYPASSWORDRESET": "Login Richtlinie: Passwort vergessen Link nicht anzeigen - benutzerdefiniert", - "LOGINPOLICYREGISTRATION": "Login Richtlinie: Registration erlauben - benutzerdefiniert", - "LOGINPOLICYIDP": "Login Richtlinie: Identity Providers - benutzerdefiniert", - "LOGINPOLICYFACTORS": "Login Richtlinie: Multifaktoren - benutzerdefiniert", - "LOGINPOLICYPASSWORDLESS": "Login Richtlinie: Passwortlose Authentifizierung - benutzerdefiniert", - "LOGINPOLICYCOMPLEXITYPOLICY": "Passwortkomplexitäts Richtlinie - benutzerdefiniert", - "LABELPOLICYPRIVATELABEL": "Label Richtlinie - benutzerdefiniert", - "LABELPOLICYWATERMARK": "Label Richtlinie - Wasserzeichen", - "CUSTOMDOMAIN": "Domänen Verifikation - verfügbar", - "CUSTOMTEXT": "Benutzerdefinierte Texte", - "PRIVACYPOLICY":"Datenschutzrichtlinie und AGB - benutzerdefiniert" + "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" }, "TIERSTATES": { "0": "Aktiv", @@ -660,7 +691,7 @@ "2": "Annuliert", "3": "Durch IAM Owner gesetzt" }, - "NOTAVAILABLE": "Feature {{value}} ist auf Ihrer Organisation nicht freigeschaltet!", + "NOTAVAILABLE": "Feature {{value}} ist auf Ihrer Organisation nicht freigeschaltet!", "RETENTIONDAYS": "Tage" }, "POLICY": { @@ -692,7 +723,9 @@ "RELEASE":"Jetzt loslassen", "DROPFONT":"Fontdatei hier ablegen", "RELEASEFONT":"Jetzt loslassen", + "USEOFLOGO":"Ihr Logo wird im Login sowie emails verwendet, während das Icon für kleinere UI-Elemente wie den Organisationswechsel in der Konsole verwendet wird", "MAXSIZE":"Die maximale Grösse von Uploads ist mit 524kB begrenzt", + "EMAILNOSVG":"Das SVG Dateiformat wird nicht in emails unterstützt. Laden Sie deshalb ihr Logo im PNG oder einem anderen unterstützten Format hoch.", "MAXSIZEEXCEEDED":"Maximale Grösse von 524kB überschritten", "FONTINLOGINONLY":"Die Schriftart wird momentan nur im Login interface angezeigt.", "PREVIEW": { @@ -898,6 +931,7 @@ "PAGES": { "TITLE": "Projekt", "DESCRIPTION": "Hier kannst Du wichtige Einstellungen prüfen und die Daten einsehen, mit denen das Projekt konfiguriert worden ist.", + "DELETE":"Projekt löschen", "LIST": "Projekte", "LISTDESCRIPTION": "Hier findest Du alle Projekte, für die Du Aktionen anzeigen oder ausführen darfst. Wenn Du Dein Projekt nicht finden kannst, wende Dich an einen Projektbesitzer oder an jemanden mit den entsprechenden Rechten, um Projektzugriff zu erhalten.", "DETAIL": "Details", @@ -962,6 +996,11 @@ "2": "Berechtigt" }, "NAME": "Name", + "NAMEDIALOG": { + "TITLE":"Projekt umbenennen", + "DESCRIPTION":"Geben Sie den neuen Namen für Ihr Projekt an!", + "NAME":"Projektname" + }, "MEMBER": { "TITLE": "Manager", "TITLEDESC": "Manager können Änderungen an diesem Projekt vornehmen, wenn sie die nötigen Rollen haben.", @@ -1207,13 +1246,18 @@ } }, "APP": { - "LIST": "Anwendungen", + "LIST": "Apps", "PAGES": { "TITLE": "Anwendung", - "DESCRIPTION": "Hier kannst Du Deine Anwendungen bearbeiten und deren Konfiguration anpassen.", + "DESCRIPTION": "Hier kannst Du Deine Applikationen bearbeiten und deren Konfiguration anpassen.", "CREATE_OIDC": "OIDC-Anwendung", "CREATE_OIDC_DESC_TITLE": "Gebe die Daten der Anwendung Schritt für Schritt ein.", "CREATE_OIDC_DESC_SUB": "Es wird automatisch eine empfohlene Konfiguration generiert.", + "STATE":"Status", + "DATECREATED":"Erstellt", + "DATECHANGED":"Geändert", + "URLS":"Urls", + "DELETE":"App löschen", "DETAIL": { "TITLE": "Detail", "STATE": { @@ -1243,6 +1287,11 @@ } } }, + "NAMEDIALOG": { + "TITLE":"App umbenennen", + "DESCRIPTION":"Geben Sie den neuen Namen für Ihre App an!", + "NAME":"Appname" + }, "NAME": "Name", "TYPE": "Anwendungstyp", "AUTHMETHOD": "Authentifizierungsmethode", diff --git a/console/src/assets/i18n/en.json b/console/src/assets/i18n/en.json index 79b25daabf..110a48ec69 100644 --- a/console/src/assets/i18n/en.json +++ b/console/src/assets/i18n/en.json @@ -106,7 +106,11 @@ } }, "ACTIONS": { + "ACTIONS":"Aktionen", + "RENAME":"Rename", "SET":"Set", + "COPY":"Copy to Clipboard", + "COPIED":"Copied to clipboard.", "RESETDEFAULT":"Reset to Default", "RESETTO":"Reset to: ", "RESETCURRENT":"Reset to current", @@ -141,7 +145,8 @@ "SEND": "Send", "NEWVALUE": "New Value", "RESTORE":"Restore", - "CONTINUEWITHOUTSAVE":"Continue without saving" + "CONTINUEWITHOUTSAVE":"Continue without saving", + "GOTOFEATURES":"Go to features" }, "RESOURCEID": "Resource Id", "TABLE": { @@ -168,16 +173,19 @@ "MY": "My Informations", "LOGINNAMES": "Loginnames", "LOGINNAMESDESC": "These are your login names:", - "COPY": "Copy to Clipboard", - "COPIED": "Copied to clipboard.", "NOUSER": "No associated users.", "REACTIVATE": "Reactivate", "DEACTIVATE": "Deactivate", "FILTER": "Filter", + "STATE":"Status", "DELETE": "Delete User", "UNLOCK": "Unlock User", "LOCKEDDESCRIPTION":"This user has been locked out due to exceeding the maximum login attempts and must be unlocked to be used again." }, + "DETAILS": { + "DATECREATED":"Created", + "DATECHANGED":"Changed" + }, "DIALOG": { "DELETE_TITLE": "Delete User", "DELETE_DESCRIPTION": "You are about to permanently delete a user. Are you sure?" @@ -243,6 +251,17 @@ "NEW":"Add New" } }, + "METADATA": { + "TITLE":"Metadata", + "DESCRIPTION":"", + "KEY":"Key", + "VALUE":"Value", + "ADD":"New Entry", + "SAVE":"Save", + "EMPTY":"Keine Metadaten", + "SETSUCCESS":"Element saved successfully", + "REMOVESUCCESS":"Element deleted successfully" + }, "MFA": { "TABLETYPE": "Type", "TABLESTATE": "Status", @@ -287,8 +306,8 @@ "USERDISPLAYNAME": "External Name", "EXTERNALUSERID": "External User ID", "DIALOG": { - "REMOVE_TITLE": "Remove IDP", - "REMOVE_DESCRIPTION": "You are about to delete an Identity Provider from a user. Do you really want to continue?" + "DELETE_TITLE": "Remove IDP", + "DELETE_DESCRIPTION": "You are about to delete an Identity Provider from a user. Do you really want to continue?" } }, "CREATE": { @@ -562,7 +581,6 @@ "ORGDETAIL_TITLE": "Enter the name and domain of your new organisation.", "ORGDETAIL_TITLE_WITHOUT_DOMAIN": "Enter the name of your new organisation.", "ORGDETAILUSER_TITLE": "Configure Organisation Owner", - "CUSTOMDOMAINFEATUREMISSING":"The Feature custom-domain is not active on your organization!", "ORGDOMAIN": { "TITLE": "Organisation Domain Ownership Verification", "VERIFICATION": "Verify the ownership of your domain. You need to download a verification file and upload it at the provided URL listed below, or place a TXT Record DNS entry for the provided URL. To complete, click the button to verify.", @@ -623,6 +641,8 @@ "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", @@ -639,20 +659,31 @@ "QUESTIONS": "For questions regarding payments contact our support ", "BTN": "Change Tier" }, + "HEADERS": { + "LOGINPOLICY":"Login Policy", + "PASSWORD":"Password", + "LABELPOLICY":"Privatelabelling", + "DOMAIN":"Organization Domain", + "TEXTSANDLINKS":"Texts and Links", + "METADATA":"Metadata" + }, "DATA": { "AUDITLOGRETENTION": "Audit Log Retention", - "LOGINPOLICYUSERNAMELOGIN": "Login Policy: Allow login with Username - custom", - "LOGINPOLICYPASSWORDRESET": "Login Policy: Hide reset password link - custom", - "LOGINPOLICYREGISTRATION": "Login Policy: Allow self registration - custom", - "LOGINPOLICYIDP": "Login Policy: Identity Provider - custom", - "LOGINPOLICYFACTORS": "Login Policy: Multifactors - custom", - "LOGINPOLICYPASSWORDLESS": "Login Policy: Passwordless Authentication - custom", - "LOGINPOLICYCOMPLEXITYPOLICY": "Password Complexity Policy - custom", - "LABELPOLICYPRIVATELABEL": "Label Policy - custom", - "LABELPOLICYWATERMARK": "Label Policy - watermark", - "CUSTOMDOMAIN": "Domain Verification - available", - "CUSTOMTEXT": "Custom texts", - "PRIVACYPOLICY":"Privacy Policy and TOS - custom" + "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" }, "TIERSTATES": { "0": "Active", @@ -660,7 +691,7 @@ "2": "Cancelled", "3": "Grandfathered" }, - "NOTAVAILABLE": "Feature {{value}} is missing on your organization.", + "NOTAVAILABLE": "Feature {{value}} is missing on your organization.", "RETENTIONDAYS": "Days" }, "POLICY": { @@ -692,7 +723,9 @@ "RELEASE":"Release", "DROPFONT":"Drop fontfile here", "RELEASEFONT":"Release", + "USEOFLOGO":"Your Logo will be used in the Login as well as emails, while the icon is used for smaller UI elements like in the organisation switcher in console", "MAXSIZE":"The maximum size is limited to 524kB", + "EMAILNOSVG":"The SVG file format is not supported in emails. Therefore upload your logo in PNG or other supported format.", "MAXSIZEEXCEEDED":"Maximum size of 524kB exceeded.", "FONTINLOGINONLY":"The font is currently only displayed in the login interface.", "PREVIEW": { @@ -900,6 +933,7 @@ "PAGES": { "TITLE": "Project", "DESCRIPTION": "Here you can define applications, manage roles and grant other organisations to use your project.", + "DELETE":"Delete Project", "LIST": "Projects", "LISTDESCRIPTION": "Here you can find all projects you are allowed to view or perform actions on. If you can't find a project, contact a project owner or someone with the corresponding rights to gain project access.", "DETAIL": "Detail", @@ -964,6 +998,11 @@ "2": "Granted" }, "NAME": "Name", + "NAMEDIALOG": { + "TITLE":"Rename Project", + "DESCRIPTION":"Enter the new name for your project", + "NAME":"New Name" + }, "MEMBER": { "TITLE": "Managers", "TITLEDESC": "Managers can make changes to this project based on their role.", @@ -1216,6 +1255,11 @@ "CREATE_OIDC": "OIDC Application", "CREATE_OIDC_DESC_TITLE": "Enter Your Application Details Step by Step", "CREATE_OIDC_DESC_SUB": "A recommended configuration will be automatically generated.", + "STATE":"State", + "DATECREATED":"Created", + "DATECHANGED":"Changed", + "URLS":"Urls", + "DELETE":"Delete App", "DETAIL": { "TITLE": "Detail", "STATE": { @@ -1246,6 +1290,11 @@ } } }, + "NAMEDIALOG": { + "TITLE":"Rename App", + "DESCRIPTION":"Enter the new name for your app", + "NAME":"New Name" + }, "NAME": "Name", "TYPE": "Application Type", "AUTHMETHOD": "Authentication Method", diff --git a/console/src/component-themes.scss b/console/src/component-themes.scss index 5c94f31133..2252452252 100644 --- a/console/src/component-themes.scss +++ b/console/src/component-themes.scss @@ -22,6 +22,7 @@ @import 'src/app/modules/zitadel-tier/zitadel-tier.component.scss'; @import 'src/app/modules/onboarding/onboarding.component.scss'; @import 'src/app/modules/policies/private-labeling-policy/private-labeling-policy.component.scss'; +@import 'src/app/modules/info-row/info-row.component.scss'; @mixin component-themes($theme) { @include avatar-theme($theme); @@ -48,4 +49,5 @@ @include private-label-theme($theme); @include owned-project-grid-theme($theme); @include granted-project-grid-theme($theme); + @include info-row-theme($theme); } diff --git a/console/src/styles.scss b/console/src/styles.scss index ed3a8c359f..e916f60cf9 100644 --- a/console/src/styles.scss +++ b/console/src/styles.scss @@ -17,6 +17,8 @@ :root { --grey: #8795a1; + --warn: #ff3b5b; + --success: #10b981; --table-row-back: #363738; } @@ -338,12 +340,19 @@ $custom-typography: mat.define-typography-config($font-family: 'Lato'); border-radius: 6px !important; } +.mat-menu-item { + line-height: 35px !important; + height: 35px !important; +} + .light-theme { @include component-themes($caos-light-app-theme); @include mat.all-component-themes($caos-light-app-theme); - --grey: #697386; --color-main: var(--theme-light-primary-500); + --warn: #cd3d56; + --success: #10b981; + $background: map-get($caos-light-app-theme, background); .sidenav, @@ -382,6 +391,8 @@ $custom-typography: mat.define-typography-config($font-family: 'Lato'); @include mat.all-component-themes($caos-dark-app-theme); --color-main: var(--theme-dark-primary-500); + --warn: #ff3b5b; + --success: #10b981; $background: map-get($caos-dark-app-theme, background); .sidenav, @@ -471,8 +482,8 @@ h2 { } @media only screen and (max-width: 500px) { - padding-left: .5rem; - padding-right: .5rem; + padding-left: 1rem; + padding-right: 1rem; } } @@ -480,6 +491,7 @@ h2 { padding: 0 1.5rem; padding-top: 2rem; padding-left: 2rem; + max-width: 1690px; @media only screen and (max-width: 500px) { padding-left: 1rem; diff --git a/internal/ui/login/static/resources/themes/scss/styles/theming/palette.scss b/internal/ui/login/static/resources/themes/scss/styles/theming/palette.scss index 19ba7309f5..57c677ed1c 100644 --- a/internal/ui/login/static/resources/themes/scss/styles/theming/palette.scss +++ b/internal/ui/login/static/resources/themes/scss/styles/theming/palette.scss @@ -262,4 +262,4 @@ $lgn-dark-theme-foreground: ( text: white, qrcode: black, google-text: #8b8d8d, -); \ No newline at end of file +); diff --git a/internal/ui/login/static/resources/themes/scss/styles/vars.scss b/internal/ui/login/static/resources/themes/scss/styles/vars.scss index 549711cda7..1659d5ab5a 100644 --- a/internal/ui/login/static/resources/themes/scss/styles/vars.scss +++ b/internal/ui/login/static/resources/themes/scss/styles/vars.scss @@ -30,16 +30,16 @@ --zitadel-color-secondary-900: #293bb9; --zitadel-color-secondary-contrast: var(--zitadel-color-white); - --zitadel-color-warn-50: #ffebee; - --zitadel-color-warn-100: #ffcdd2; - --zitadel-color-warn-200: #ef9a9a; - --zitadel-color-warn-300: #e57373; - --zitadel-color-warn-400: #ef5350; - --zitadel-color-warn-500: #f44336; - --zitadel-color-warn-600: #e53935; - --zitadel-color-warn-700: #d32f2f; - --zitadel-color-warn-800: #c62828; - --zitadel-color-warn-900: #b71c1c; + --zitadel-color-warn-50: #F9E7EB; + --zitadel-color-warn-100: #F0C4CC; + --zitadel-color-warn-200: #E69DAB; + --zitadel-color-warn-300: #DC7689; + --zitadel-color-warn-400: #D5586F; + --zitadel-color-warn-500: #CD3B56; + --zitadel-color-warn-600: #C8354F; + --zitadel-color-warn-700: #C12D45; + --zitadel-color-warn-800: #BA263C; + --zitadel-color-warn-900: #AE192B; --zitadel-font-family: 'Lato'; @@ -137,16 +137,16 @@ --zitadel-color-secondary-800: #305687; --zitadel-color-secondary-900: #284770; - --zitadel-color-warn-50: #ffebee; - --zitadel-color-warn-100: #ffcdd2; - --zitadel-color-warn-200: #ef9a9a; - --zitadel-color-warn-300: #e57373; - --zitadel-color-warn-400: #ef5350; - --zitadel-color-warn-500: #f44336; - --zitadel-color-warn-600: #e53935; - --zitadel-color-warn-700: #d32f2f; - --zitadel-color-warn-800: #c62828; - --zitadel-color-warn-900: #b71c1c; + --zitadel-color-warn-50: #FFE7EB; + --zitadel-color-warn-100: #FFC4CE; + --zitadel-color-warn-200: #FF9DAD; + --zitadel-color-warn-300: #FF768C; + --zitadel-color-warn-400: #FF5874; + --zitadel-color-warn-500: #ff3b5b; + --zitadel-color-warn-600: #FF3553; + --zitadel-color-warn-700: #FF2D49; + --zitadel-color-warn-800: #FF2640; + --zitadel-color-warn-900: #FF192F; --zitadel-font-family: 'Lato'; diff --git a/internal/ui/login/static/resources/themes/scss/zitadel-alternative.scss b/internal/ui/login/static/resources/themes/scss/zitadel-alternative.scss index fa04e5f535..945d940b58 100644 --- a/internal/ui/login/static/resources/themes/scss/zitadel-alternative.scss +++ b/internal/ui/login/static/resources/themes/scss/zitadel-alternative.scss @@ -79,21 +79,54 @@ $lgn-alternative-light-brand: ( ) ); -$lgn-alternative-warn: ( - 50: #ffebee, - 100: #ffcdd2, - 200: #ef9a9a, - 300: #e57373, - 400: #ef5350, - 500: #f44336, - 600: #e53935, - 700: #d32f2f, - 800: #c62828, - 900: #b71c1c, - A100: #ff8a80, - A200: #ff5252, - A400: #ff1744, - A700: #d50000, +$lgn-alternative-light-warn: ( + 50: #F9E8EB, + 100: #F0C5CC, + 200: #E69EAB, + 300: #DC7789, + 400: #D55A6F, + 500: #CD3D56, + 600: #C8374F, + 700: #C12F45, + 800: #BA273C, + 900: #AE1A2B, + A100: #FFE4E6, + A200: #FFB1B9, + A400: #FF7E8B, + A700: #FF6474, + contrast: ( + 50: $dark-text, + 100: $dark-text, + 200: $dark-text, + 300: $dark-text, + 400: $dark-text, + 500: $light-text, + 600: $light-text, + 700: $light-text, + 800: $light-text, + 900: $light-text, + A100: $dark-text, + A200: $light-text, + A400: $light-text, + A700: $light-text, + ) +); + +$lgn-alternative-dark-warn: ( + 50: #FFE7EB, + 100: #FFC4CE, + 200: #FF9DAD, + 300: #FF768C, + 400: #FF5874, + 500: #FF3B5B, + 600: #FF3553, + 700: #FF2D49, + 800: #FF2640, + 900: #FF192F, + A100: #FFFFFF, + A200: #FFFDFD, + A400: #FFCACE, + A700: #FFB1B7, contrast: ( 50: $dark-text, 100: $dark-text, @@ -114,11 +147,11 @@ $lgn-alternative-warn: ( $light-primary: lgn-palette($lgn-alternative-light-brand); $light-accent: lgn-palette($lgn-alternative-light-brand); -$light-warn: lgn-palette($lgn-alternative-warn); +$light-warn: lgn-palette($lgn-alternative-light-warn); $dark-primary: lgn-palette($lgn-alternative-dark-brand); $dark-accent: lgn-palette($lgn-alternative-dark-brand); -$dark-warn: lgn-palette($lgn-alternative-warn); +$dark-warn: lgn-palette($lgn-alternative-dark-warn); $light-theme: lgn-light-theme($light-primary, $light-accent, $light-warn); $dark-theme: lgn-dark-theme($dark-primary, $dark-accent, $dark-warn); diff --git a/internal/ui/login/static/resources/themes/zitadel/css/zitadel.css b/internal/ui/login/static/resources/themes/zitadel/css/zitadel.css index 4d9b8cbcb3..52c4a88388 100644 --- a/internal/ui/login/static/resources/themes/zitadel/css/zitadel.css +++ b/internal/ui/login/static/resources/themes/zitadel/css/zitadel.css @@ -28,16 +28,16 @@ --zitadel-color-secondary-800: #3a4cc3; --zitadel-color-secondary-900: #293bb9; --zitadel-color-secondary-contrast: var(--zitadel-color-white); - --zitadel-color-warn-50: #ffebee; - --zitadel-color-warn-100: #ffcdd2; - --zitadel-color-warn-200: #ef9a9a; - --zitadel-color-warn-300: #e57373; - --zitadel-color-warn-400: #ef5350; - --zitadel-color-warn-500: #f44336; - --zitadel-color-warn-600: #e53935; - --zitadel-color-warn-700: #d32f2f; - --zitadel-color-warn-800: #c62828; - --zitadel-color-warn-900: #b71c1c; + --zitadel-color-warn-50: #F9E7EB; + --zitadel-color-warn-100: #F0C4CC; + --zitadel-color-warn-200: #E69DAB; + --zitadel-color-warn-300: #DC7689; + --zitadel-color-warn-400: #D5586F; + --zitadel-color-warn-500: #CD3B56; + --zitadel-color-warn-600: #C8354F; + --zitadel-color-warn-700: #C12D45; + --zitadel-color-warn-800: #BA263C; + --zitadel-color-warn-900: #AE192B; --zitadel-font-family: "Lato"; --zitadel-color-background-50: rgb(255, 255, 255); --zitadel-color-background-100: rgb(255, 255, 255); @@ -118,16 +118,16 @@ --zitadel-color-secondary-700: #38649d; --zitadel-color-secondary-800: #305687; --zitadel-color-secondary-900: #284770; - --zitadel-color-warn-50: #ffebee; - --zitadel-color-warn-100: #ffcdd2; - --zitadel-color-warn-200: #ef9a9a; - --zitadel-color-warn-300: #e57373; - --zitadel-color-warn-400: #ef5350; - --zitadel-color-warn-500: #f44336; - --zitadel-color-warn-600: #e53935; - --zitadel-color-warn-700: #d32f2f; - --zitadel-color-warn-800: #c62828; - --zitadel-color-warn-900: #b71c1c; + --zitadel-color-warn-50: #FFE7EB; + --zitadel-color-warn-100: #FFC4CE; + --zitadel-color-warn-200: #FF9DAD; + --zitadel-color-warn-300: #FF768C; + --zitadel-color-warn-400: #FF5874; + --zitadel-color-warn-500: #ff3b5b; + --zitadel-color-warn-600: #FF3553; + --zitadel-color-warn-700: #FF2D49; + --zitadel-color-warn-800: #FF2640; + --zitadel-color-warn-900: #FF192F; --zitadel-font-family: "Lato"; --zitadel-color-background-50: rgb(60, 60, 60); --zitadel-color-background-100: rgb(55, 55, 55);
{{ 'USER.MFA.TABLETYPE' | translate }}