diff --git a/cmd/zitadel/setup.yaml b/cmd/zitadel/setup.yaml index c3a4fcfb9..0643eae9e 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 786531dd6..f98547b0a 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 a0e815d0e..15a7a2717 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 000000000..7cdf22bcb --- /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 000000000..b0a5d68b7 --- /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 2fc80e263..c9db8863e 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 a64e3cdd9..2f959d780 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 4c310fec5..24f64aa97 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 468d33308..3ec921e80 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 000000000..51ddb2f0c --- /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 000000000..6d9425eba --- /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 000000000..2e31c5bd2 --- /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 000000000..67e5051df --- /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 000000000..886dd7088 --- /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 4de828cb3..42b3912cd 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 dd478bd42..9c92ed6b4 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 0126c8011..938bb2670 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 894e0b250..c0b84382f 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 af0435b76..cacd027c0 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 cf96272ca..8843220e5 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 b9eaf2be5..0aafd4494 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 e168359e3..900ca3c22 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 4ff4da366..c3d79b8cb 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 fda07728b..dc481a67d 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 000000000..590eb7a4a --- /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 000000000..01bb81ffe --- /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 000000000..f65ec4f12 --- /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 000000000..551c5133c --- /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 000000000..fa24ad3ad --- /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 e72abd118..0531f30b5 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 2dfcd1d56..b83f9e6e6 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 a0bc1b2e3..298b158eb 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 e87b8343a..d5dac47b7 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 015a9536b..297a8a6da 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 76c95df88..08a6c943c 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 455fa0b45..b2705ea8f 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 d72bd450a..700a4e949 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 add68cc30..5d0a95a94 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 6d2e3ed7f..57262b7af 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 3724c9aa6..a7ccd70da 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 ddbb32ec3..532ed28b3 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 9fb7ff0c3..4b6cd08aa 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 1c5f0f85a..44b1872ca 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 818d7f1c8..2dbdd2b14 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 000000000..a7c363432 --- /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 000000000..ac5a6377e --- /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 000000000..483b8ae75 --- /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 000000000..fbab147a3 --- /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 000000000..85b2b465e --- /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 000000000..595d6451d --- /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 000000000..d3e10ecb7 --- /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 000000000..e0a716f42 --- /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 715ad845e..80ccfb7e6 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 899d9d1e1..47dcd292c 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 5cb25ad59..89a49f91f 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 9ab9f1752..e6468e50e 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 88632f79b..22daeb0bc 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 78f698e84..48198197d 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 c06b9553b..026a63178 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 415be2126..85a557481 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 7abeeb71a..323eba015 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 4305f0552..db65123d6 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 30b95e02d..4509bc565 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 79b25daab..110a48ec6 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 5c94f3113..225245225 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 ed3a8c359..e916f60cf 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 19ba7309f..57c677ed1 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 549711cda..1659d5ab5 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 fa04e5f53..945d940b5 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 4d9b8cbcb..52c4a8838 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 }}