fix(console): table pagination, diverse optimizations, lint workspace (#1563)

* show app redirect issues, action layout

* refactor pagination

* tos, privacy

* new paginator everywhere

* layout width and table optim

* lint pt1

* lint config, ts

* stylelint

* chore(deps-dev): bump @angular/cli from 11.2.7 to 11.2.8 in /console (#1566)

* fix: remove app name check on ChangeAPIApplication (#1561)

* fix: remove app name check on ChangeAPIApplication

* fix APIConfigInvalid message

* chore(workflow): ignore tags for docs and codecov flows (#1564)

* chore(deps-dev): bump @angular/cli from 11.2.7 to 11.2.8 in /console

Bumps [@angular/cli](https://github.com/angular/angular-cli) from 11.2.7 to 11.2.8.
- [Release notes](https://github.com/angular/angular-cli/releases)
- [Commits](https://github.com/angular/angular-cli/compare/v11.2.7...v11.2.8)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: Livio Amstutz <livio.a@gmail.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps-dev): bump jasmine-spec-reporter from 6.0.0 to 7.0.0 in /console (#1573)

* fix: remove app name check on ChangeAPIApplication (#1561)

* fix: remove app name check on ChangeAPIApplication

* fix APIConfigInvalid message

* chore(workflow): ignore tags for docs and codecov flows (#1564)

* chore(deps-dev): bump jasmine-spec-reporter in /console

Bumps [jasmine-spec-reporter](https://github.com/bcaudan/jasmine-spec-reporter) from 6.0.0 to 7.0.0.
- [Release notes](https://github.com/bcaudan/jasmine-spec-reporter/releases)
- [Changelog](https://github.com/bcaudan/jasmine-spec-reporter/blob/master/CHANGELOG.md)
- [Commits](https://github.com/bcaudan/jasmine-spec-reporter/compare/v6.0.0...v7.0.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: Livio Amstutz <livio.a@gmail.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps-dev): bump @angular-devkit/build-angular from 0.1102.7 to 0.1102.8 in /console (#1574)

* fix: remove app name check on ChangeAPIApplication (#1561)

* fix: remove app name check on ChangeAPIApplication

* fix APIConfigInvalid message

* chore(workflow): ignore tags for docs and codecov flows (#1564)

* chore(deps-dev): bump @angular-devkit/build-angular in /console

Bumps [@angular-devkit/build-angular](https://github.com/angular/angular-cli) from 0.1102.7 to 0.1102.8.
- [Release notes](https://github.com/angular/angular-cli/releases)
- [Commits](https://github.com/angular/angular-cli/commits)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: Livio Amstutz <livio.a@gmail.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Max Peintner <max@caos.ch>

* chore(deps): bump @types/file-saver from 2.0.1 to 2.0.2 in /console (#1576)

* fix: remove app name check on ChangeAPIApplication (#1561)

* fix: remove app name check on ChangeAPIApplication

* fix APIConfigInvalid message

* chore(workflow): ignore tags for docs and codecov flows (#1564)

* chore(deps): bump @types/file-saver from 2.0.1 to 2.0.2 in /console

Bumps [@types/file-saver](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/file-saver) from 2.0.1 to 2.0.2.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/file-saver)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: Livio Amstutz <livio.a@gmail.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps-dev): bump @angular/language-service from 11.2.8 to 11.2.9 in /console (#1567)

* fix: remove app name check on ChangeAPIApplication (#1561)

* fix: remove app name check on ChangeAPIApplication

* fix APIConfigInvalid message

* chore(workflow): ignore tags for docs and codecov flows (#1564)

* chore(deps-dev): bump @angular/language-service in /console

Bumps [@angular/language-service](https://github.com/angular/angular/tree/HEAD/packages/language-service) from 11.2.8 to 11.2.9.
- [Release notes](https://github.com/angular/angular/releases)
- [Changelog](https://github.com/angular/angular/blob/master/CHANGELOG.md)
- [Commits](https://github.com/angular/angular/commits/11.2.9/packages/language-service)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: Livio Amstutz <livio.a@gmail.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* update protobuf, libphone

* chore(deps): bump rxjs from 6.6.3 to 6.6.7 in /console (#1488)

Bumps [rxjs](https://github.com/reactivex/rxjs) from 6.6.3 to 6.6.7.
- [Release notes](https://github.com/reactivex/rxjs/releases)
- [Changelog](https://github.com/ReactiveX/rxjs/blob/6.6.7/CHANGELOG.md)
- [Commits](https://github.com/reactivex/rxjs/compare/6.6.3...6.6.7)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps-dev): bump @types/jasmine from 3.6.3 to 3.6.9 in /console (#1485)

Bumps [@types/jasmine](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/jasmine) from 3.6.3 to 3.6.9.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/jasmine)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Max Peintner <max@caos.ch>

* chore(deps-dev): bump jasmine-core from 3.6.0 to 3.7.1 in /console (#1445)

Bumps [jasmine-core](https://github.com/jasmine/jasmine) from 3.6.0 to 3.7.1.
- [Release notes](https://github.com/jasmine/jasmine/releases)
- [Changelog](https://github.com/jasmine/jasmine/blob/main/RELEASE.md)
- [Commits](https://github.com/jasmine/jasmine/compare/v3.6.0...v3.7.1)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Max Peintner <max@caos.ch>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Livio Amstutz <livio.a@gmail.com>
This commit is contained in:
Max Peintner 2021-04-15 15:54:10 +02:00 committed by GitHub
parent b0681a0bbe
commit 56b527060b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
142 changed files with 2058 additions and 1588 deletions

View File

@ -74,21 +74,19 @@ RUN build/console/generate-grpc.sh
FROM scratch as npm-copy FROM scratch as npm-copy
COPY --from=npm-base /console/src/app/proto/generated ./console/src/app/proto/generated COPY --from=npm-base /console/src/app/proto/generated ./console/src/app/proto/generated
####################### #######################
## anular dev build ## angular dev build
####################### #######################
FROM npm-base as dev-angular-build FROM npm-base as dev-angular-build
RUN npm install -g @angular/cli RUN npm install -g @angular/cli
####################### #######################
## anular prod build ## angular lint workspace and prod build
####################### #######################
FROM npm-base as prod-angular-build FROM npm-base as prod-angular-build
RUN npm run lint
RUN npm run prodbuild RUN npm run prodbuild
####################### #######################
## Go dependencies ## Go dependencies
## Speed up this step by mounting your local go mod pkg directory ## Speed up this step by mounting your local go mod pkg directory

View File

@ -115,7 +115,8 @@
"tsconfig.spec.json" "tsconfig.spec.json"
], ],
"exclude": [ "exclude": [
"**/node_modules/**" "**/node_modules/**",
"**/proto/generated/**"
] ]
} }
}, },

File diff suppressed because it is too large Load Diff

View File

@ -24,7 +24,7 @@
"@angular/service-worker": "~11.0.0", "@angular/service-worker": "~11.0.0",
"@ngx-translate/core": "^13.0.0", "@ngx-translate/core": "^13.0.0",
"@ngx-translate/http-loader": "^6.0.0", "@ngx-translate/http-loader": "^6.0.0",
"@types/file-saver": "^2.0.1", "@types/file-saver": "^2.0.2",
"@types/google-protobuf": "^3.7.4", "@types/google-protobuf": "^3.7.4",
"@types/uuid": "^8.3.0", "@types/uuid": "^8.3.0",
"angular-oauth2-oidc": "^10.0.3", "angular-oauth2-oidc": "^10.0.3",
@ -32,29 +32,29 @@
"cors": "^2.8.5", "cors": "^2.8.5",
"file-saver": "^2.0.5", "file-saver": "^2.0.5",
"google-proto-files": "^2.4.0", "google-proto-files": "^2.4.0",
"google-protobuf": "^3.15.7", "google-protobuf": "^3.15.8",
"grpc": "^1.24.5", "grpc": "^1.24.5",
"grpc-web": "^1.2.1", "grpc-web": "^1.2.1",
"libphonenumber-js": "^1.9.13", "libphonenumber-js": "^1.9.16",
"moment": "^2.29.1", "moment": "^2.29.1",
"ngx-quicklink": "^0.2.6", "ngx-quicklink": "^0.2.6",
"rxjs": "~6.6.3", "rxjs": "~6.6.7",
"ts-protoc-gen": "^0.14.0", "ts-protoc-gen": "^0.14.0",
"tslib": "^2.2.0", "tslib": "^2.2.0",
"uuid": "^8.3.2", "uuid": "^8.3.2",
"zone.js": "~0.11.3" "zone.js": "~0.11.3"
}, },
"devDependencies": { "devDependencies": {
"@angular/cli": "~11.2.7", "@angular-devkit/build-angular": "~0.1102.8",
"@angular-devkit/build-angular": "~0.1102.7", "@angular/cli": "~11.2.8",
"@angular/compiler-cli": "~11.0.0", "@angular/compiler-cli": "~11.0.0",
"@types/jasmine": "~3.6.3", "@types/jasmine": "~3.6.9",
"@angular/language-service": "~11.2.8", "@angular/language-service": "~11.2.9",
"@types/jasminewd2": "~2.0.3", "@types/jasminewd2": "~2.0.3",
"@types/node": "^14.14.37", "@types/node": "^14.14.37",
"codelyzer": "^6.0.0", "codelyzer": "^6.0.0",
"jasmine-core": "~3.6.0", "jasmine-core": "~3.7.1",
"jasmine-spec-reporter": "~6.0.0", "jasmine-spec-reporter": "~7.0.0",
"karma": "~6.3.2", "karma": "~6.3.2",
"karma-chrome-launcher": "~3.1.0", "karma-chrome-launcher": "~3.1.0",
"karma-coverage-istanbul-reporter": "~3.0.2", "karma-coverage-istanbul-reporter": "~3.0.2",

View File

@ -18,7 +18,7 @@ const routes: Routes = [
canActivate: [AuthGuard, RoleGuard], canActivate: [AuthGuard, RoleGuard],
data: { data: {
roles: ['iam.write'], roles: ['iam.write'],
} },
}, },
{ {
path: 'granted-projects', path: 'granted-projects',

View File

@ -179,8 +179,12 @@
<span class="fill-space"></span> <span class="fill-space"></span>
<div class="toc-line"> <div class="toc-line">
<a class="toc" href="https://zitadel.ch/pdf/agb.pdf" alt="Terms and Conditions" <a class="toc" [href]="language == 'de' ? 'https://zitadel.ch/pdf/agb.pdf' : 'https://zitadel.ch/pdf/tos.pdf'" alt="Terms and Conditions"
target="_blank">{{'MENU.TOC' target="_blank">{{'MENU.TOS'
| translate}}</a>
<span class="slash">|</span>
<a class="toc" [href]="language == 'de' ? 'https://zitadel.ch/pdf/datenschutz.pdf' : 'https://zitadel.ch/pdf/privacy.pdf'" alt="Terms and Conditions"
target="_blank">{{'MENU.PRIVACY'
| translate}}</a> | translate}}</a>
<span>&nbsp;&nbsp;&nbsp;</span> <span>&nbsp;&nbsp;&nbsp;</span>
</div> </div>

View File

@ -12,8 +12,8 @@
right: 0; right: 0;
.org-button { .org-button {
font-weight: bold; font-weight: bold;
padding-right: .5rem; padding-right: .5rem;
} }
.logo { .logo {
@ -168,36 +168,18 @@
margin: 2rem 2rem; margin: 2rem 2rem;
.toc { .toc {
font-size: 12px; font-size: 12px;
color: var(--grey); color: var(--grey);
text-decoration: none; text-decoration: none;
&:hover { &:hover {
text-decoration: underline; text-decoration: underline;
} }
} }
.sp-status { .slash {
font-size: 12px; margin: 0 .5rem;
color: var(--grey); color: var(--grey);
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
.sp-status .sp-status-badge.sp-status-ok {
background: darkgreen;
}
.sp-status .sp-status-badge.sp-status-scheduled {
background: darkblue;
}
.sp-status .sp-status-badge.sp-status-minor {
background: darkorange;
}
.sp-status .sp-status-badge.sp-status-major {
background: darkred;
} }
} }

View File

@ -58,6 +58,7 @@ export class AppComponent implements OnDestroy {
private orgSub: Subscription = new Subscription(); private orgSub: Subscription = new Subscription();
public hideAdminWarn: boolean = true; public hideAdminWarn: boolean = true;
public language: string = 'en';
constructor( constructor(
public viewPortScroller: ViewportScroller, public viewPortScroller: ViewportScroller,
@Inject('windowObject') public window: Window, @Inject('windowObject') public window: Window,
@ -196,6 +197,7 @@ export class AppComponent implements OnDestroy {
this.translate.onLangChange.subscribe((language: LangChangeEvent) => { this.translate.onLangChange.subscribe((language: LangChangeEvent) => {
this.document.documentElement.lang = language.lang; this.document.documentElement.lang = language.lang;
this.language = language.lang;
}); });
this.filterControl.valueChanges.pipe(debounceTime(300)).subscribe(value => { this.filterControl.valueChanges.pipe(debounceTime(300)).subscribe(value => {
@ -266,8 +268,12 @@ export class AppComponent implements OnDestroy {
const cropped = navigator.language.split('-')[0] ?? 'en'; const cropped = navigator.language.split('-')[0] ?? 'en';
const fallbackLang = cropped.match(/en|de/) ? cropped : 'en'; const fallbackLang = cropped.match(/en|de/) ? cropped : 'en';
const lang = userprofile?.human?.profile?.preferredLanguage.match(/en|de/) ? userprofile.human.profile?.preferredLanguage : fallbackLang; const lang =
userprofile?.human?.profile?.preferredLanguage.match(/en|de/) ?
userprofile.human.profile?.preferredLanguage :
fallbackLang;
this.translate.use(lang); this.translate.use(lang);
this.language = lang;
this.document.documentElement.lang = lang; this.document.documentElement.lang = lang;
} }
}); });

View File

@ -4,8 +4,8 @@ import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { KeyType } from 'src/app/proto/generated/zitadel/auth_n_key_pb'; import { KeyType } from 'src/app/proto/generated/zitadel/auth_n_key_pb';
export enum AddKeyDialogType { export enum AddKeyDialogType {
MACHINE = "MACHINE", MACHINE = 'MACHINE',
AUTHNKEY = "AUTHNKEY", AUTHNKEY = 'AUTHNKEY',
} }
@Component({ @Component({

View File

@ -100,8 +100,7 @@ export class MemberCreateDialogComponent {
if (project.projectId && project.grantId) { if (project.projectId && project.grantId) {
this.projectId = project.projectId; this.projectId = project.projectId;
this.grantId = project.grantId; this.grantId = project.grantId;
} } else if (project.id) {
else if (project.id) {
this.projectId = project.id; this.projectId = project.id;
} }
} }

View File

@ -10,46 +10,46 @@
/* stylelint-enable */ /* stylelint-enable */
.cnsl-app-card { .cnsl-app-card {
padding: 1rem; padding: 1rem;
box-sizing: border-box; box-sizing: border-box;
cursor: pointer; cursor: pointer;
animation: all .2s; animation: all .2s;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
font-size: 2rem; font-size: 2rem;
height: 80px; height: 80px;
width: 80px; width: 80px;
margin: 1rem; margin: 1rem;
text-transform: uppercase; text-transform: uppercase;
border-radius: .5rem; border-radius: .5rem;
font-weight: 800; font-weight: 800;
background-color: $primary-dark; background-color: $primary-dark;
transition: background-color box-shadow .3s ease-in; transition: background-color box-shadow .3s ease-in;
&.web { &.web {
background-color: rgb(80, 110, 110); background-color: rgb(80, 110, 110);
color: white; color: white;
border: none; border: none;
}
&.native {
background-color: #595d80;
color: white;
border: none;
}
&.useragent {
background-color: #6a506e;
color: white;
border: none;
}
&.api {
background-color: #333;
color: white;
border: none;
}
} }
&.native {
background-color: #595d80;
color: white;
border: none;
}
&.useragent {
background-color: #6a506e;
color: white;
border: none;
}
&.api {
background-color: #333;
color: white;
border: none;
}
}
} }

View File

@ -2,14 +2,14 @@
@import '~@angular/material/theming'; @import '~@angular/material/theming';
.radio-button-wrapper { .radio-button-wrapper {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
flex-wrap: nowrap; flex-wrap: nowrap;
overflow-x: auto; overflow-x: auto;
overflow-y: hidden; overflow-y: hidden;
margin: 0; margin: 0;
padding-bottom: .5rem; padding-bottom: .5rem;
padding-top: 1rem; padding-top: 1rem;
} }
@mixin app-auth-method-radio-theme($theme) { @mixin app-auth-method-radio-theme($theme) {
@ -17,7 +17,7 @@
$primary-color: mat-color($primary, 500); $primary-color: mat-color($primary, 500);
$is-dark-theme: map-get($theme, is-dark); $is-dark-theme: map-get($theme, is-dark);
input[type="radio"]{ input[type="radio"] {
appearance: none; appearance: none;
opacity: 0; opacity: 0;
display: none; display: none;
@ -27,7 +27,7 @@
border-color: if($is-dark-theme, white, var(--grey)); border-color: if($is-dark-theme, white, var(--grey));
.cnsl-radio-header span { .cnsl-radio-header span {
color: if($is-dark-theme, white, white); color: if($is-dark-theme, white, white);
} }
} }
@ -43,98 +43,98 @@
padding-bottom: 1rem; padding-bottom: 1rem;
box-shadow: inset 0 0 6px rgba(0, 0, 0, .1); box-shadow: inset 0 0 6px rgba(0, 0, 0, .1);
&.first { &.first {
margin-left: 0; margin-left: 0;
} }
&.last { &.last {
margin-right: 0; margin-right: 0;
} }
.recommended { .recommended {
position: absolute; position: absolute;
bottom: 0; bottom: 0;
left: 50%; left: 50%;
transform: translateY(50%) translateX(-50%); transform: translateY(50%) translateX(-50%);
border-radius: 50vw; border-radius: 50vw;
font-size: 11px; font-size: 11px;
background: white; background: white;
color: black; color: black;
padding: 3px 1rem; padding: 3px 1rem;
box-shadow: 0 0 6px rgb(0 0 0 / 10%); box-shadow: 0 0 6px rgb(0 0 0 / 10%);
white-space: nowrap; white-space: nowrap;
&.not { &.not {
background: rgb(144 75 75); background: rgb(144 75 75);
color: white; color: white;
} }
} }
.cnsl-radio-header { .cnsl-radio-header {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
flex-direction: column; flex-direction: column;
background: rgb(80, 110, 110); background: rgb(80, 110, 110);
border-top-left-radius: 6px; border-top-left-radius: 6px;
border-top-right-radius: 6px; border-top-right-radius: 6px;
position: relative; position: relative;
.current { .current {
position: absolute; position: absolute;
bottom: .5rem; bottom: .5rem;
left: 50%; left: 50%;
transform: translateX(-50%); transform: translateX(-50%);
display: block; display: block;
color: #ffffff60; color: #ffffff60;
white-space: nowrap; white-space: nowrap;
font-size: 12px; font-size: 12px;
} }
span { span {
margin: 2rem; margin: 2rem;
font-size: 30px; font-size: 30px;
color: if($is-dark-theme,#21222450, #ffffff50); color: if($is-dark-theme, #21222450, #ffffff50);
} }
} }
p { p {
text-align: center; text-align: center;
padding: 0 1rem; padding: 0 1rem;
} }
.type-desc { .type-desc {
font-size: 14px; font-size: 14px;
color: var(--grey); color: var(--grey);
} }
.fill-space { .fill-space {
flex: 1; flex: 1;
} }
.app-specs { .app-specs {
display: block; display: block;
padding: 1rem 0; padding: 1rem 0;
margin: 0 1rem; margin: 0 1rem;
.row { .row {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
font-size: 12px; font-size: 12px;
color: var(--grey); color: var(--grey);
margin: 3px 0; margin: 3px 0;
span { span {
white-space: nowrap; white-space: nowrap;
}
:first-child {
margin-right: 1rem;
overflow: hidden;
text-overflow: ellipsis;
}
} }
:first-child {
margin-right: 1rem;
overflow: hidden;
text-overflow: ellipsis;
}
}
} }
} }
} }

View File

@ -10,7 +10,7 @@ export interface RadioItemAuthType {
key: string; key: string;
titleI18nKey: string; titleI18nKey: string;
descI18nKey: string; descI18nKey: string;
disabled: boolean, disabled: boolean;
prefix: string; prefix: string;
background: string; background: string;
responseType?: OIDCResponseType; responseType?: OIDCResponseType;
@ -36,4 +36,4 @@ export class AppAuthMethodRadioComponent {
public emitChange(): void { public emitChange(): void {
this.selectedMethod.emit(this.selected); this.selectedMethod.emit(this.selected);
} }
} }

View File

@ -2,18 +2,18 @@
@import '~@angular/material/theming'; @import '~@angular/material/theming';
.radio-button-wrapper { .radio-button-wrapper {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
flex-wrap: wrap; flex-wrap: wrap;
margin: 0 -0.5rem; margin: 0 -.5rem;
} }
@mixin app-type-radio-theme($theme) { @mixin app-type-radio-theme($theme) {
$primary: map-get($theme, primary); $primary: map-get($theme, primary);
$primary-color: mat-color($primary, 500); $primary-color: mat-color($primary, 500);
$is-dark-theme: map-get($theme, is-dark); $is-dark-theme: map-get($theme, is-dark);
input[type="radio"]{ input[type="radio"] {
appearance: none; appearance: none;
opacity: 0; opacity: 0;
display: none; display: none;
@ -23,7 +23,7 @@
border-color: if($is-dark-theme, white, var(--grey)); border-color: if($is-dark-theme, white, var(--grey));
.cnsl-type-radio-header span { .cnsl-type-radio-header span {
color: if($is-dark-theme, white, white); color: if($is-dark-theme, white, white);
} }
} }
@ -40,29 +40,29 @@
box-shadow: inset 0 0 6px rgba(0, 0, 0, .1); box-shadow: inset 0 0 6px rgba(0, 0, 0, .1);
.cnsl-type-radio-header { .cnsl-type-radio-header {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
background: rgb(80, 110, 110); background: rgb(80, 110, 110);
margin-bottom: 1rem; margin-bottom: 1rem;
border-top-left-radius: 6px; border-top-left-radius: 6px;
border-top-right-radius: 6px; border-top-right-radius: 6px;
span { span {
margin: 2rem; margin: 2rem;
font-size: 30px; font-size: 30px;
color: if($is-dark-theme,#21222450, #ffffff50); color: if($is-dark-theme, #21222450, #ffffff50);
} }
} }
p { p {
text-align: center; text-align: center;
padding: 0 1rem; padding: 0 1rem;
} }
.type-desc { .type-desc {
font-size: 14px; font-size: 14px;
color: var(--grey); color: var(--grey);
} }
} }
} }

View File

@ -14,4 +14,4 @@ export class AppTypeRadioComponent {
public emitChange(): void { public emitChange(): void {
this.selectedType.emit(this.selected); this.selectedType.emit(this.selected);
} }
} }

View File

@ -21,123 +21,120 @@
} }
@mixin changes-theme($theme) { @mixin changes-theme($theme) {
$is-dark-theme: map-get($theme, is-dark); $is-dark-theme: map-get($theme, is-dark);
.scroll-container { .scroll-container {
max-height: 50vh; max-height: 50vh;
overflow-y: scroll; overflow-y: scroll;
border-bottom: 1px solid if($is-dark-theme, #303131, #e3e8ee); border-bottom: 1px solid if($is-dark-theme, #303131, #e3e8ee);
/* stylelint-enable */ margin-bottom: .5rem;
margin-bottom: 0.5rem;
.date { .date {
font-weight: 500; font-weight: 500;
font-size: 0.8rem; font-size: .8rem;
display: block; display: block;
margin-bottom: .5rem; margin-bottom: .5rem;
}
.item {
display: block;
padding: 10px 0;
font-size: 0.8rem;
.row {
display: flex;
flex-direction: row;
.spacer {
width: 32px;
}
.actions {
flex: 1;
display: flex;
flex-direction: column;
margin-top: -0.5rem;
.action {
display: flex;
flex-direction: row;
align-items: center;
flex: 1;
.icon {
width: 32px;
display: inline-block;
height: 1.2rem;
line-height: 1.2rem;
font-size: 1.2rem;
color: var(--grey);
}
span {
flex: 1;
font-weight: 500;
font-size: 0.8rem;
overflow-x: hidden;
}
.msg {
text-overflow: ellipsis;
}
.block {
display: block;
}
.restore {
visibility: hidden;
display: none;
opacity: 0;
margin-left: 1rem;
transform: opacity 0.2s ease-in-out;
}
&:hover {
.restore {
visibility: visible;
display: inline-block;
opacity: 1;
color: #81868a;
&[disabled] {
visibility: hidden;
}
&:hover {
color: white;
}
}
}
}
}
/* stylelint-disable */
$primary: map-get($theme, primary);
$primary-dark: mat-color($primary, A800);
/* stylelint-enable */
&.change-item-back {
background-color: rgba($primary-dark, .93);
transition: background-color .3s cubic-bezier(.645, .045, .355, 1);
}
}
.sp-wrapper {
padding: .5rem;
display: flex;
justify-content: center;
}
}
.end-container {
font-size: 12px;
color: var(--grey);
font-size: 14px;
margin: 1rem 0 1rem 0;
display: block;
color: var(--grey);
}
} }
}
.item {
display: block;
padding: 10px 0;
font-size: .8rem;
.row {
display: flex;
flex-direction: row;
.spacer {
width: 32px;
}
.actions {
flex: 1;
display: flex;
flex-direction: column;
margin-top: -.5rem;
.action {
display: flex;
flex-direction: row;
align-items: center;
flex: 1;
.icon {
width: 32px;
display: inline-block;
height: 1.2rem;
line-height: 1.2rem;
font-size: 1.2rem;
color: var(--grey);
}
span {
flex: 1;
font-weight: 500;
font-size: .8rem;
overflow-x: hidden;
}
.msg {
text-overflow: ellipsis;
}
.block {
display: block;
}
.restore {
visibility: hidden;
display: none;
opacity: 0;
margin-left: 1rem;
transform: opacity .2s ease-in-out;
}
&:hover {
.restore {
visibility: visible;
display: inline-block;
opacity: 1;
color: #81868a;
&[disabled] {
visibility: hidden;
}
&:hover {
color: white;
}
}
}
}
}
/* stylelint-disable */
$primary: map-get($theme, primary);
$primary-dark: mat-color($primary, A800);
/* stylelint-enable */
&.change-item-back {
background-color: rgba($primary-dark, .93);
transition: background-color .3s cubic-bezier(.645, .045, .355, 1);
}
}
.sp-wrapper {
padding: .5rem;
display: flex;
justify-content: center;
}
}
.end-container {
font-size: 14px;
margin: 1rem 0 1rem 0;
display: block;
color: var(--grey);
}
}
}

View File

@ -23,7 +23,7 @@ export enum ChangeType {
} }
export interface MappedChange { export interface MappedChange {
key: string, key: string;
values: Array<{ values: Array<{
data: any[]; data: any[];
dates: Timestamp.AsObject[]; dates: Timestamp.AsObject[];
@ -34,7 +34,11 @@ export interface MappedChange {
}>; }>;
} }
type ListChanges = ListMyUserChangesResponse.AsObject | ListUserChangesResponse.AsObject | ListProjectChangesResponse.AsObject | ListOrgChangesResponse.AsObject | ListAppChangesResponse.AsObject; type ListChanges = ListMyUserChangesResponse.AsObject |
ListUserChangesResponse.AsObject |
ListProjectChangesResponse.AsObject |
ListOrgChangesResponse.AsObject |
ListAppChangesResponse.AsObject;
@Component({ @Component({
selector: 'app-changes', selector: 'app-changes',
@ -171,11 +175,14 @@ export class ChangesComponent implements OnInit, OnDestroy {
} }
} }
mapChanges(changes: Change.AsObject[]) { private mapChanges(changes: Change.AsObject[]): {
key: string; values: any[];
}[] {
const splitted: { [editorId: string]: any[]; } = {}; const splitted: { [editorId: string]: any[]; } = {};
changes.forEach((change) => { changes.forEach((change) => {
if (change.changeDate) { if (change.changeDate) {
const index = `${this.getDateString(change.changeDate)}`;//`${this.getDateString(change.changeDate)}:${change.editorId}`; const index = `${this.getDateString(change.changeDate)}`;
// `${this.getDateString(change.changeDate)}:${change.editorId}`;
if (index) { if (index) {
if (splitted[index]) { if (splitted[index]) {
@ -209,7 +216,7 @@ export class ChangesComponent implements OnInit, OnDestroy {
// data: [change.data], // data: [change.data],
eventTypes: [change.eventType], eventTypes: [change.eventType],
sequences: [change.sequence], sequences: [change.sequence],
} },
]; ];
} }
} }
@ -226,7 +233,7 @@ export class ChangesComponent implements OnInit, OnDestroy {
return arr; return arr;
} }
getDateString(ts: Timestamp.AsObject) { getDateString(ts: Timestamp.AsObject): string {
const date = new Date(ts.seconds * 1000 + ts.nanos / 1000 / 1000); const date = new Date(ts.seconds * 1000 + ts.nanos / 1000 / 1000);
return date.getUTCFullYear() + this.pad(date.getUTCMonth() + 1) + this.pad(date.getUTCDate()); return date.getUTCFullYear() + this.pad(date.getUTCMonth() + 1) + this.pad(date.getUTCDate());
} }
@ -243,10 +250,10 @@ export class ChangesComponent implements OnInit, OnDestroy {
// Order by ascending property value // Order by ascending property value
valueAscOrder = (a: KeyValue<number, string>, b: KeyValue<number, string>): number => { valueAscOrder = (a: KeyValue<number, string>, b: KeyValue<number, string>): number => {
return a.value.localeCompare(b.value); return a.value.localeCompare(b.value);
}; }
// Order by descending property key // Order by descending property key
keyDescOrder = (a: KeyValue<number, string>, b: KeyValue<number, string>): number => { keyDescOrder = (a: KeyValue<number, string>, b: KeyValue<number, string>): number => {
return a.key > b.key ? -1 : (b.key > a.key ? 1 : 0); return a.key > b.key ? -1 : (b.key > a.key ? 1 : 0);
}; }
} }

View File

@ -31,7 +31,7 @@ import { ChangesComponent } from './changes.component';
LocalizedDatePipeModule, LocalizedDatePipeModule,
TimestampToDatePipeModule, TimestampToDatePipeModule,
MatTooltipModule, MatTooltipModule,
AvatarModule AvatarModule,
], ],
exports: [ exports: [
ChangesComponent, ChangesComponent,

View File

@ -65,7 +65,7 @@
</tr> </tr>
</table> </table>
<mat-paginator #paginator class="paginator" [length]="keyResult?.details?.totalResult || 0" [pageSize]="10" <cnsl-paginator #paginator class="paginator" [timestamp]="keyResult?.details?.viewTimestamp" [length]="keyResult?.details?.totalResult || 0" [pageSize]="10"
[pageSizeOptions]="[5, 10, 20]" (page)="changePage($event)"></mat-paginator> [pageSizeOptions]="[5, 10, 20]" (page)="changePage($event)"></cnsl-paginator>
</div> </div>
</app-refresh-table> </app-refresh-table>

View File

@ -1,7 +1,6 @@
import { SelectionModel } from '@angular/cdk/collections'; import { SelectionModel } from '@angular/cdk/collections';
import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core'; import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { MatPaginator, PageEvent } from '@angular/material/paginator';
import { MatTableDataSource } from '@angular/material/table'; import { MatTableDataSource } from '@angular/material/table';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb'; import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb';
@ -14,6 +13,8 @@ import { ListAppKeysResponse } from 'src/app/proto/generated/zitadel/management_
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
import { ToastService } from 'src/app/services/toast.service'; import { ToastService } from 'src/app/services/toast.service';
import { PageEvent, PaginatorComponent } from '../paginator/paginator.component';
@Component({ @Component({
selector: 'app-client-keys', selector: 'app-client-keys',
templateUrl: './client-keys.component.html', templateUrl: './client-keys.component.html',
@ -23,7 +24,7 @@ export class ClientKeysComponent implements OnInit {
@Input() projectId!: string; @Input() projectId!: string;
@Input() appId!: string; @Input() appId!: string;
@ViewChild(MatPaginator) public paginator!: MatPaginator; @ViewChild(PaginatorComponent) public paginator!: PaginatorComponent;
public dataSource: MatTableDataSource<Key.AsObject> = new MatTableDataSource<Key.AsObject>(); public dataSource: MatTableDataSource<Key.AsObject> = new MatTableDataSource<Key.AsObject>();
public selection: SelectionModel<Key.AsObject> = new SelectionModel<Key.AsObject>(true, []); public selection: SelectionModel<Key.AsObject> = new SelectionModel<Key.AsObject>(true, []);
public keyResult!: ListAppKeysResponse.AsObject; public keyResult!: ListAppKeysResponse.AsObject;
@ -95,7 +96,12 @@ export class ClientKeysComponent implements OnInit {
} }
if (type) { if (type) {
return this.mgmtService.addAppKey(this.projectId, this.appId, type, date ? date : undefined).then((response) => { return this.mgmtService.addAppKey(
this.projectId,
this.appId,
type,
date ? date : undefined,
).then((response) => {
if (response) { if (response) {
setTimeout(() => { setTimeout(() => {
this.refreshPage(); this.refreshPage();
@ -104,7 +110,7 @@ export class ClientKeysComponent implements OnInit {
this.dialog.open(ShowKeyDialogComponent, { this.dialog.open(ShowKeyDialogComponent, {
data: { data: {
key: response, key: response,
type: AddKeyDialogType.AUTHNKEY type: AddKeyDialogType.AUTHNKEY,
}, },
width: '400px', width: '400px',
}); });

View File

@ -5,24 +5,24 @@ import { MatButtonModule } from '@angular/material/button';
import { MatCheckboxModule } from '@angular/material/checkbox'; import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatDialogModule } from '@angular/material/dialog'; import { MatDialogModule } from '@angular/material/dialog';
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
import { MatPaginatorModule } from '@angular/material/paginator';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatTableModule } from '@angular/material/table'; import { MatTableModule } from '@angular/material/table';
import { MatTooltipModule } from '@angular/material/tooltip'; import { MatTooltipModule } from '@angular/material/tooltip';
import { RouterModule } from '@angular/router';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module'; import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module';
import { CardModule } from '../card/card.module';
import { InputModule } from '../input/input.module';
import { RefreshTableModule } from '../refresh-table/refresh-table.module';
import { ClientKeysComponent } from './client-keys.component';
import { ShowKeyDialogModule } from '../show-key-dialog/show-key-dialog.module';
import { AddKeyDialogModule } from 'src/app/modules/add-key-dialog/add-key-dialog.module'; import { AddKeyDialogModule } from 'src/app/modules/add-key-dialog/add-key-dialog.module';
import { RouterModule } from '@angular/router'; import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module';
import { LocalizedDatePipeModule } from 'src/app/pipes/localized-date-pipe/localized-date-pipe.module'; import { 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 { TimestampToDatePipeModule } from 'src/app/pipes/timestamp-to-date-pipe/timestamp-to-date-pipe.module';
import { CardModule } from '../card/card.module';
import { InputModule } from '../input/input.module';
import { PaginatorModule } from '../paginator/paginator.module';
import { RefreshTableModule } from '../refresh-table/refresh-table.module';
import { ShowKeyDialogModule } from '../show-key-dialog/show-key-dialog.module';
import { ClientKeysComponent } from './client-keys.component';
@NgModule({ @NgModule({
declarations: [ declarations: [
@ -37,7 +37,7 @@ import { TimestampToDatePipeModule } from 'src/app/pipes/timestamp-to-date-pipe/
HasRoleModule, HasRoleModule,
CardModule, CardModule,
MatTableModule, MatTableModule,
MatPaginatorModule, PaginatorModule,
MatIconModule, MatIconModule,
MatProgressSpinnerModule, MatProgressSpinnerModule,
MatCheckboxModule, MatCheckboxModule,

View File

@ -20,7 +20,7 @@ import { FeaturesComponent } from './features.component';
@NgModule({ @NgModule({
declarations: [ declarations: [
FeaturesComponent FeaturesComponent,
], ],
imports: [ imports: [
FeaturesRoutingModule, FeaturesRoutingModule,
@ -41,6 +41,6 @@ import { FeaturesComponent } from './features.component';
], ],
exports: [ exports: [
FeaturesComponent, FeaturesComponent,
] ],
}) })
export class FeaturesModule { } export class FeaturesModule { }

View File

@ -82,7 +82,7 @@ export class IdpCreateComponent implements OnInit, OnDestroy {
} }
public addIdp(): void { public addIdp(): void {
if (this.serviceType == PolicyComponentServiceType.MGMT) { if (this.serviceType === PolicyComponentServiceType.MGMT) {
const req = new AddOrgOIDCIDPRequest(); const req = new AddOrgOIDCIDPRequest();
req.setName(this.name?.value); req.setName(this.name?.value);

View File

@ -31,7 +31,7 @@
[disabled]="serviceType==PolicyComponentServiceType.MGMT && idp?.owner == IDPOwnerType.IDP_OWNER_TYPE_SYSTEM" [disabled]="serviceType==PolicyComponentServiceType.MGMT && idp?.owner == IDPOwnerType.IDP_OWNER_TYPE_SYSTEM"
(change)="$event ? selection.toggle(idp) : null" [checked]="selection.isSelected(idp)"> (change)="$event ? selection.toggle(idp) : null" [checked]="selection.isSelected(idp)">
<img src="../../../assets/images/google.png" <img src="../../../assets/images/google.png"
*ngIf="idp.stylingType == IdpStylingType.IDPSTYLINGTYPE_GOOGLE" alt="google" /> *ngIf="idp.stylingType == IDPSTYLINGTYPE.IDPSTYLINGTYPE_GOOGLE" alt="google" />
</mat-checkbox> </mat-checkbox>
</td> </td>
</ng-container> </ng-container>
@ -100,6 +100,6 @@
</tr> </tr>
</table> </table>
</div> </div>
<mat-paginator #paginator class="paginator" [length]="idpResult?.details?.totalResult || 0" [pageSize]="10" <cnsl-paginator #paginator class="paginator" [timestamp]="idpResult?.details?.viewTimestamp" [length]="idpResult?.details?.totalResult || 0" [pageSize]="10"
[pageSizeOptions]="[5, 10, 20]" (page)="changePage($event)"></mat-paginator> [pageSizeOptions]="[5, 10, 20]" (page)="changePage($event)"></cnsl-paginator>
</app-refresh-table> </app-refresh-table>

View File

@ -1,6 +1,7 @@
.ar-button { .ar-button {
margin-right: .5rem; margin-right: .5rem;
} }
.table-wrapper { .table-wrapper {
overflow: auto; overflow: auto;
width: 100%; width: 100%;
@ -27,12 +28,12 @@ td {
outline: none; outline: none;
img { img {
height: 30px; height: 30px;
width: 30px; width: 30px;
margin-left: 1rem; margin-left: 1rem;
border-radius: .5rem; border-radius: .5rem;
object-fit: contain; object-fit: contain;
margin-top: .5rem; margin-top: .5rem;
} }
} }
@ -100,15 +101,13 @@ tr {
} }
} }
.date-block { .date-block {
margin: .5rem 0; margin: .5rem 0;
display: block;
min-width: 120px;
.date-sub {
font-size: 13px;
display: block; display: block;
min-width: 120px; }
.date-sub {
font-size: 13px;
display: block;
}
} }

View File

@ -1,7 +1,6 @@
import { SelectionModel } from '@angular/cdk/collections'; import { SelectionModel } from '@angular/cdk/collections';
import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core'; import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { MatPaginator, PageEvent } from '@angular/material/paginator';
import { MatTableDataSource } from '@angular/material/table'; import { MatTableDataSource } from '@angular/material/table';
import { RouterLink } from '@angular/router'; import { RouterLink } from '@angular/router';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
@ -13,6 +12,7 @@ import { AdminService } from 'src/app/services/admin.service';
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
import { ToastService } from 'src/app/services/toast.service'; import { ToastService } from 'src/app/services/toast.service';
import { PageEvent, PaginatorComponent } from '../paginator/paginator.component';
import { PolicyComponentServiceType } from '../policies/policy-component-types.enum'; import { PolicyComponentServiceType } from '../policies/policy-component-types.enum';
import { WarnDialogComponent } from '../warn-dialog/warn-dialog.component'; import { WarnDialogComponent } from '../warn-dialog/warn-dialog.component';
@ -25,7 +25,7 @@ export class IdpTableComponent implements OnInit {
@Input() public serviceType!: PolicyComponentServiceType; @Input() public serviceType!: PolicyComponentServiceType;
@Input() service!: AdminService | ManagementService; @Input() service!: AdminService | ManagementService;
@Input() disabled: boolean = false; @Input() disabled: boolean = false;
@ViewChild(MatPaginator) public paginator!: MatPaginator; @ViewChild(PaginatorComponent) public paginator!: PaginatorComponent;
public dataSource: MatTableDataSource<IDP.AsObject> public dataSource: MatTableDataSource<IDP.AsObject>
= new MatTableDataSource<IDP.AsObject>(); = new MatTableDataSource<IDP.AsObject>();
public selection: SelectionModel<IDP.AsObject> public selection: SelectionModel<IDP.AsObject>
@ -36,7 +36,7 @@ export class IdpTableComponent implements OnInit {
public PolicyComponentServiceType: any = PolicyComponentServiceType; public PolicyComponentServiceType: any = PolicyComponentServiceType;
public IDPOwnerType: any = IDPOwnerType; public IDPOwnerType: any = IDPOwnerType;
public IDPState: any = IDPState; public IDPState: any = IDPState;
public IdpStylingType: any = IDPStylingType; public IDPSTYLINGTYPE: any = IDPStylingType;
@Input() public displayedColumns: string[] = ['select', 'name', 'config', 'dates', 'state']; @Input() public displayedColumns: string[] = ['select', 'name', 'config', 'dates', 'state'];
@Output() public changedSelection: EventEmitter<Array<IDP.AsObject>> @Output() public changedSelection: EventEmitter<Array<IDP.AsObject>>

View File

@ -4,7 +4,6 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { MatCheckboxModule } from '@angular/material/checkbox'; import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
import { MatPaginatorModule } from '@angular/material/paginator';
import { MatTableModule } from '@angular/material/table'; import { MatTableModule } from '@angular/material/table';
import { MatTooltipModule } from '@angular/material/tooltip'; import { MatTooltipModule } from '@angular/material/tooltip';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
@ -15,6 +14,7 @@ import { LocalizedDatePipeModule } from 'src/app/pipes/localized-date-pipe/local
import { TimestampToDatePipeModule } from 'src/app/pipes/timestamp-to-date-pipe/timestamp-to-date-pipe.module'; import { TimestampToDatePipeModule } from 'src/app/pipes/timestamp-to-date-pipe/timestamp-to-date-pipe.module';
import { TruncatePipeModule } from 'src/app/pipes/truncate-pipe/truncate-pipe.module'; import { TruncatePipeModule } from 'src/app/pipes/truncate-pipe/truncate-pipe.module';
import { PaginatorModule } from '../paginator/paginator.module';
import { IdpTableComponent } from './idp-table.component'; import { IdpTableComponent } from './idp-table.component';
@NgModule({ @NgModule({
@ -31,7 +31,7 @@ import { IdpTableComponent } from './idp-table.component';
LocalizedDatePipeModule, LocalizedDatePipeModule,
TimestampToDatePipeModule, TimestampToDatePipeModule,
MatTableModule, MatTableModule,
MatPaginatorModule, PaginatorModule,
RouterModule, RouterModule,
RefreshTableModule, RefreshTableModule,
HasRoleModule, HasRoleModule,

View File

@ -26,8 +26,8 @@
min-width: 150px; min-width: 150px;
.chip { .chip {
border-radius: .5rem; border-radius: .5rem;
height: 40px; height: 40px;
} }
&.fullwidth { &.fullwidth {
@ -40,21 +40,21 @@
} }
.line { .line {
display: flex; display: flex;
align-items: flex-end; align-items: flex-end;
width: 100%; width: 100%;
.formfield { .formfield {
flex: 1; flex: 1;
input { input {
margin: 0; margin: 0;
}
} }
}
button { button {
margin-bottom: 12px; margin-bottom: 12px;
} }
} }
} }

View File

@ -80,7 +80,7 @@ export class IdpComponent implements OnInit, OnDestroy {
})).subscribe((params) => { })).subscribe((params) => {
const { id } = params; const { id } = params;
if (id) { if (id) {
if (this.serviceType == PolicyComponentServiceType.MGMT) { if (this.serviceType === PolicyComponentServiceType.MGMT) {
(this.service as ManagementService).getOrgIDPByID(id).then(resp => { (this.service as ManagementService).getOrgIDPByID(id).then(resp => {
if (resp.idp) { if (resp.idp) {
const idpObject = resp.idp; const idpObject = resp.idp;
@ -90,7 +90,7 @@ export class IdpComponent implements OnInit, OnDestroy {
} }
} }
}); });
} else if (this.serviceType == PolicyComponentServiceType.ADMIN) { } else if (this.serviceType === PolicyComponentServiceType.ADMIN) {
(this.service as AdminService).getIDPByID(id).then(resp => { (this.service as AdminService).getIDPByID(id).then(resp => {
if (resp.idp) { if (resp.idp) {
const idpObject = resp.idp; const idpObject = resp.idp;
@ -118,7 +118,7 @@ export class IdpComponent implements OnInit, OnDestroy {
} }
public updateIdp(): void { public updateIdp(): void {
if (this.serviceType == PolicyComponentServiceType.MGMT) { if (this.serviceType === PolicyComponentServiceType.MGMT) {
const req = new UpdateOrgIDPRequest(); const req = new UpdateOrgIDPRequest();
req.setIdpId(this.id?.value); req.setIdpId(this.id?.value);
@ -131,7 +131,7 @@ export class IdpComponent implements OnInit, OnDestroy {
}).catch(error => { }).catch(error => {
this.toast.showError(error); this.toast.showError(error);
}); });
} else if (this.serviceType == PolicyComponentServiceType.ADMIN) { } else if (this.serviceType === PolicyComponentServiceType.ADMIN) {
const req = new UpdateIDPRequest(); const req = new UpdateIDPRequest();
req.setIdpId(this.id?.value); req.setIdpId(this.id?.value);
@ -148,7 +148,7 @@ export class IdpComponent implements OnInit, OnDestroy {
} }
public updateOidcConfig(): void { public updateOidcConfig(): void {
if (this.serviceType == PolicyComponentServiceType.MGMT) { if (this.serviceType === PolicyComponentServiceType.MGMT) {
const req = new UpdateOrgIDPOIDCConfigRequest(); const req = new UpdateOrgIDPOIDCConfigRequest();
req.setIdpId(this.id?.value); req.setIdpId(this.id?.value);
@ -165,7 +165,7 @@ export class IdpComponent implements OnInit, OnDestroy {
}).catch(error => { }).catch(error => {
this.toast.showError(error); this.toast.showError(error);
}); });
} else if (this.serviceType == PolicyComponentServiceType.ADMIN) { } else if (this.serviceType === PolicyComponentServiceType.ADMIN) {
const req = new UpdateIDPOIDCConfigRequest(); const req = new UpdateIDPOIDCConfigRequest();
req.setIdpId(this.id?.value); req.setIdpId(this.id?.value);

View File

@ -13,33 +13,33 @@
font-size: 14px; font-size: 14px;
.icon { .icon {
margin-right: 1rem; margin-right: 1rem;
height: 1.2rem; height: 1.2rem;
line-height: 1.2rem; line-height: 1.2rem;
font-size: 1.2rem; font-size: 1.2rem;
margin-left: .5rem; margin-left: .5rem;
} }
.info-section-content { .info-section-content {
flex: 1; flex: 1;
} }
&.info { &.info {
background-color: if($is-dark-theme, #4f566b, #cbf4c9); background-color: if($is-dark-theme, #4f566b, #cbf4c9);
color: if($is-dark-theme, #cbf4c9, #0e6245); color: if($is-dark-theme, #cbf4c9, #0e6245);
.icon { .icon {
color: $primary-color; color: $primary-color;
} }
} }
&.warn { &.warn {
background-color: if($is-dark-theme, #4f566b, #ffc1c1); background-color: if($is-dark-theme, #4f566b, #ffc1c1);
color: if($is-dark-theme, #ffc1c1, #620e0e); color: if($is-dark-theme, #ffc1c1, #620e0e);
.icon { .icon {
color: if($is-dark-theme, #ffc1c1, #620e0e); color: if($is-dark-theme, #ffc1c1, #620e0e);
} }
} }
} }
} }

View File

@ -12,5 +12,5 @@ enum InfoSectionType {
}) })
export class InfoSectionComponent { export class InfoSectionComponent {
@Input() type = InfoSectionType.INFO; @Input() type: InfoSectionType = InfoSectionType.INFO;
} }

View File

@ -1,59 +1,59 @@
.next-steps { .next-steps {
margin-top: 1rem; margin-top: 1rem;
h5 {
text-transform: uppercase; h5 {
text-transform: uppercase;
font-size: 14px;
color: var(--grey);
}
.row {
width: 100%;
display: flex;
overflow-x: auto;
padding-bottom: .5rem;
.step {
min-width: 220px;
max-width: 280px;
padding: 1rem;
margin: 0 .5rem;
border: 1px solid var(--grey);
border-radius: .5rem;
display: flex;
flex-direction: column;
align-items: center;
box-sizing: border-box;
flex: 1;
h6 {
font-size: 1rem;
text-align: center;
margin: 0 0 1rem 0;
}
p {
font-size: 14px; font-size: 14px;
text-align: center;
color: var(--grey); color: var(--grey);
}
.fill-space {
flex: 1;
}
button {
display: block;
margin: auto;
}
&:first-child {
margin-left: 0;
}
&:last-child {
margin-right: 0;
}
} }
}
.row { }
width: 100%;
display: flex;
overflow-x: auto;
padding-bottom: .5rem;
.step {
min-width: 220px;
max-width: 280px;
padding: 1rem;
margin: 0 .5rem;
border: 1px solid var(--grey);
border-radius: .5rem;
display: flex;
flex-direction: column;
align-items: center;
box-sizing: border-box;
flex: 1;
h6 {
font-size: 1rem;
text-align: center;
margin: 0 0 1rem 0;
}
p {
font-size: 14px;
text-align: center;
color: var(--grey);
}
.fill-space {
flex: 1;
}
button {
display: block;
margin: auto;
}
&:first-child {
margin-left: 0;
}
&:last-child {
margin-right: 0;
}
}
}
}

View File

@ -8,7 +8,7 @@ describe('LinksComponent', () => {
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ LinksComponent ] declarations: [ LinksComponent ],
}) })
.compileComponents(); .compileComponents();
}); });

View File

@ -12,7 +12,7 @@ export interface CnslLinks {
@Component({ @Component({
selector: 'cnsl-links', selector: 'cnsl-links',
templateUrl: './links.component.html', templateUrl: './links.component.html',
styleUrls: ['./links.component.scss'] styleUrls: ['./links.component.scss'],
}) })
export class LinksComponent implements OnInit { export class LinksComponent implements OnInit {
@Input() links: Array<CnslLinks> = []; @Input() links: Array<CnslLinks> = [];

View File

@ -19,6 +19,6 @@ import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
], ],
exports: [ exports: [
LinksComponent, LinksComponent,
] ],
}) })
export class LinksModule { } export class LinksModule { }

View File

@ -64,7 +64,7 @@
</tr> </tr>
</table> </table>
<mat-paginator #paginator class="paginator" [length]="keyResult?.details?.totalResult || 0" [pageSize]="10" <cnsl-paginator #paginator class="paginator" [timestamp]="keyResult?.details?.viewTimestamp" [length]="keyResult?.details?.totalResult || 0" [pageSize]="10"
[pageSizeOptions]="[5, 10, 20]" (page)="changePage($event)"></mat-paginator> [pageSizeOptions]="[5, 10, 20]" (page)="changePage($event)"></cnsl-paginator>
</div> </div>
</app-refresh-table> </app-refresh-table>

View File

@ -1,7 +1,6 @@
import { SelectionModel } from '@angular/cdk/collections'; import { SelectionModel } from '@angular/cdk/collections';
import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core'; import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { MatPaginator, PageEvent } from '@angular/material/paginator';
import { MatTableDataSource } from '@angular/material/table'; import { MatTableDataSource } from '@angular/material/table';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb'; import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb';
@ -14,6 +13,8 @@ import { ListMachineKeysResponse } from 'src/app/proto/generated/zitadel/managem
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
import { ToastService } from 'src/app/services/toast.service'; import { ToastService } from 'src/app/services/toast.service';
import { PageEvent, PaginatorComponent } from '../paginator/paginator.component';
@Component({ @Component({
selector: 'app-machine-keys', selector: 'app-machine-keys',
templateUrl: './machine-keys.component.html', templateUrl: './machine-keys.component.html',
@ -22,7 +23,7 @@ import { ToastService } from 'src/app/services/toast.service';
export class MachineKeysComponent implements OnInit { export class MachineKeysComponent implements OnInit {
@Input() userId!: string; @Input() userId!: string;
@ViewChild(MatPaginator) public paginator!: MatPaginator; @ViewChild(PaginatorComponent) public paginator!: PaginatorComponent;
public dataSource: MatTableDataSource<Key.AsObject> = new MatTableDataSource<Key.AsObject>(); public dataSource: MatTableDataSource<Key.AsObject> = new MatTableDataSource<Key.AsObject>();
public selection: SelectionModel<Key.AsObject> = new SelectionModel<Key.AsObject>(true, []); public selection: SelectionModel<Key.AsObject> = new SelectionModel<Key.AsObject>(true, []);
public keyResult!: ListMachineKeysResponse.AsObject; public keyResult!: ListMachineKeysResponse.AsObject;
@ -103,7 +104,7 @@ export class MachineKeysComponent implements OnInit {
this.dialog.open(ShowKeyDialogComponent, { this.dialog.open(ShowKeyDialogComponent, {
data: { data: {
key: response, key: response,
type: AddKeyDialogType.MACHINE type: AddKeyDialogType.MACHINE,
}, },
width: '400px', width: '400px',
}); });

View File

@ -5,24 +5,24 @@ import { MatButtonModule } from '@angular/material/button';
import { MatCheckboxModule } from '@angular/material/checkbox'; import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatDialogModule } from '@angular/material/dialog'; import { MatDialogModule } from '@angular/material/dialog';
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
import { MatPaginatorModule } from '@angular/material/paginator';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatTableModule } from '@angular/material/table'; import { MatTableModule } from '@angular/material/table';
import { MatTooltipModule } from '@angular/material/tooltip'; import { MatTooltipModule } from '@angular/material/tooltip';
import { RouterModule } from '@angular/router';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module'; import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module';
import { CardModule } from '../card/card.module';
import { InputModule } from '../input/input.module';
import { RefreshTableModule } from '../refresh-table/refresh-table.module';
import { MachineKeysComponent } from './machine-keys.component';
import { ShowKeyDialogModule } from '../show-key-dialog/show-key-dialog.module';
import { AddKeyDialogModule } from 'src/app/modules/add-key-dialog/add-key-dialog.module'; import { AddKeyDialogModule } from 'src/app/modules/add-key-dialog/add-key-dialog.module';
import { RouterModule } from '@angular/router'; import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module';
import { LocalizedDatePipeModule } from 'src/app/pipes/localized-date-pipe/localized-date-pipe.module'; import { 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 { TimestampToDatePipeModule } from 'src/app/pipes/timestamp-to-date-pipe/timestamp-to-date-pipe.module';
import { CardModule } from '../card/card.module';
import { InputModule } from '../input/input.module';
import { PaginatorModule } from '../paginator/paginator.module';
import { RefreshTableModule } from '../refresh-table/refresh-table.module';
import { ShowKeyDialogModule } from '../show-key-dialog/show-key-dialog.module';
import { MachineKeysComponent } from './machine-keys.component';
@NgModule({ @NgModule({
declarations: [ declarations: [
@ -37,7 +37,7 @@ import { TimestampToDatePipeModule } from 'src/app/pipes/timestamp-to-date-pipe/
HasRoleModule, HasRoleModule,
CardModule, CardModule,
MatTableModule, MatTableModule,
MatPaginatorModule, PaginatorModule,
MatIconModule, MatIconModule,
MatProgressSpinnerModule, MatProgressSpinnerModule,
MatCheckboxModule, MatCheckboxModule,

View File

@ -91,7 +91,7 @@
</tr> </tr>
</table> </table>
</div> </div>
<mat-paginator *ngIf="dataSource" class="paginator" #paginator [pageSize]="INITIALPAGESIZE" <cnsl-paginator *ngIf="dataSource" class="paginator" #paginator [timestamp]="dataSource?.viewTimestamp" [pageSize]="INITIALPAGESIZE"
[length]="dataSource.totalResult" [pageSizeOptions]="[25, 50, 100, 250]" (page)="changePage($event)"> [length]="dataSource.totalResult" [pageSizeOptions]="[25, 50, 100, 250]" (page)="changePage($event)">
</mat-paginator> </cnsl-paginator>
</app-refresh-table> </app-refresh-table>

View File

@ -1,6 +1,5 @@
import { SelectionModel } from '@angular/cdk/collections'; import { SelectionModel } from '@angular/cdk/collections';
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output, ViewChild } from '@angular/core'; import { Component, EventEmitter, Input, OnDestroy, OnInit, Output, ViewChild } from '@angular/core';
import { MatPaginator, PageEvent } from '@angular/material/paginator';
import { MatSelectChange } from '@angular/material/select'; import { MatSelectChange } from '@angular/material/select';
import { MatTable } from '@angular/material/table'; import { MatTable } from '@angular/material/table';
import { Observable, Subject } from 'rxjs'; import { Observable, Subject } from 'rxjs';
@ -9,6 +8,7 @@ import { IamMembersDataSource } from 'src/app/pages/iam/iam-members/iam-members-
import { OrgMembersDataSource } from 'src/app/pages/orgs/org-members/org-members-datasource'; import { OrgMembersDataSource } from 'src/app/pages/orgs/org-members/org-members-datasource';
import { Member } from 'src/app/proto/generated/zitadel/member_pb'; import { Member } from 'src/app/proto/generated/zitadel/member_pb';
import { PageEvent, PaginatorComponent } from '../paginator/paginator.component';
import { ProjectMembersDataSource } from '../project-members/project-members-datasource'; import { ProjectMembersDataSource } from '../project-members/project-members-datasource';
type MemberDatasource = OrgMembersDataSource | ProjectMembersDataSource | IamMembersDataSource; type MemberDatasource = OrgMembersDataSource | ProjectMembersDataSource | IamMembersDataSource;
@ -22,7 +22,7 @@ export class MembersTableComponent implements OnInit, OnDestroy {
public INITIALPAGESIZE: number = 25; public INITIALPAGESIZE: number = 25;
@Input() public canDelete: boolean = false; @Input() public canDelete: boolean = false;
@Input() public canWrite: boolean = false; @Input() public canWrite: boolean = false;
@ViewChild(MatPaginator) public paginator!: MatPaginator; @ViewChild(PaginatorComponent) public paginator!: PaginatorComponent;
@ViewChild(MatTable) public table!: MatTable<Member.AsObject>; @ViewChild(MatTable) public table!: MatTable<Member.AsObject>;
@Input() public dataSource!: MemberDatasource; @Input() public dataSource!: MemberDatasource;
public selection: SelectionModel<any> = new SelectionModel<any>(true, []); public selection: SelectionModel<any> = new SelectionModel<any>(true, []);
@ -70,7 +70,7 @@ export class MembersTableComponent implements OnInit, OnDestroy {
this.dataSource.membersSubject.value.forEach(row => this.selection.select(row)); this.dataSource.membersSubject.value.forEach(row => this.selection.select(row));
} }
public changePage(event?: PageEvent | MatPaginator): any { public changePage(event?: PageEvent): any {
this.selection.clear(); this.selection.clear();
return this.factoryLoadFunc(event ?? this.paginator); return this.factoryLoadFunc(event ?? this.paginator);
} }

View File

@ -4,7 +4,6 @@ import { FormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { MatCheckboxModule } from '@angular/material/checkbox'; import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
import { MatPaginatorModule } from '@angular/material/paginator';
import { MatSelectModule } from '@angular/material/select'; import { MatSelectModule } from '@angular/material/select';
import { MatSortModule } from '@angular/material/sort'; import { MatSortModule } from '@angular/material/sort';
import { MatTableModule } from '@angular/material/table'; import { MatTableModule } from '@angular/material/table';
@ -14,6 +13,7 @@ import { TranslateModule } from '@ngx-translate/core';
import { InputModule } from 'src/app/modules/input/input.module'; import { InputModule } from 'src/app/modules/input/input.module';
import { AvatarModule } from '../avatar/avatar.module'; import { AvatarModule } from '../avatar/avatar.module';
import { PaginatorModule } from '../paginator/paginator.module';
import { RefreshTableModule } from '../refresh-table/refresh-table.module'; import { RefreshTableModule } from '../refresh-table/refresh-table.module';
import { MembersTableComponent } from './members-table.component'; import { MembersTableComponent } from './members-table.component';
@ -28,7 +28,7 @@ import { MembersTableComponent } from './members-table.component';
MatCheckboxModule, MatCheckboxModule,
MatIconModule, MatIconModule,
MatTableModule, MatTableModule,
MatPaginatorModule, PaginatorModule,
MatSortModule, MatSortModule,
MatTooltipModule, MatTooltipModule,
FormsModule, FormsModule,

View File

@ -2,7 +2,7 @@
@import '~@angular/material/theming'; @import '~@angular/material/theming';
@mixin meta-theme($theme) { @mixin meta-theme($theme) {
$is-dark-theme: map-get($theme, is-dark); $is-dark-theme: map-get($theme, is-dark);
.meta-details { .meta-details {
margin-bottom: 1rem; margin-bottom: 1rem;

View File

@ -45,14 +45,14 @@
&:not(.disabled) { &:not(.disabled) {
&:hover { &:hover {
.rm { .rm {
display: block; display: block;
} }
} }
} }
&.disabled { &.disabled {
opacity: .5; opacity: .5;
cursor: not-allowed; cursor: not-allowed;
} }
} }
} }

View File

@ -11,7 +11,7 @@
box-shadow: inset 0 -1px if($is-dark-theme, #303131, #e3e8ee); box-shadow: inset 0 -1px if($is-dark-theme, #303131, #e3e8ee);
.prev { .prev {
background: $primary-color; background: $primary-color;
} }
.goto { .goto {
@ -32,116 +32,116 @@
} }
.split { .split {
position: absolute; position: absolute;
top: 0; top: 0;
bottom: 0; bottom: 0;
left: 0; left: 0;
right: 0; right: 0;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
box-sizing: border-box; box-sizing: border-box;
border-radius: 0.5rem; border-radius: .5rem;
box-shadow: 0 3px 8px 0 rgb(0 0 0 / 6%); box-shadow: 0 3px 8px 0 rgb(0 0 0 / 6%);
@media only screen and (min-width: 1024px) { @media only screen and (min-width: 1024px) {
flex-direction: row; flex-direction: row;
.right {
overflow: auto;
}
}
.left {
flex-basis: 300px;
box-sizing: border-box;
padding: 1.5rem;
background: linear-gradient(40deg, rgb(80, 66, 121),rgb(177, 59, 122),rgb(225,53,81), rgb(230,107,86));
box-shadow: inset -2px 1px 15px -9px #000000;
h1 {
color: white;
}
.firststeps {
color: #fad6e3;
text-transform: uppercase;
font-size: 12px;
font-weight: bold;
}
p {
color: #fad6e3;
font-size: 12px;
font-weight: bold;
}
button {
width: 100%;
}
}
.right { .right {
padding: 1.5rem; overflow: auto;
flex: 1;
box-sizing: border-box;
.desc {
color: var(--grey);
font-size: 20px;
margin-top: .5rem;
}
.onboarding-row {
display: flex;
padding: 1rem 0;
align-items: center;
.prev {
height: 40px;
width: 40px;
min-width: 40px;
border-radius: .5rem;
display: flex;
align-items: center;
justify-content: center;
margin-right: 2rem;
color: white;
font-size: 1.2rem;
box-shadow: 0 3px 8px 0 rgb(0 0 0 / 6%);
}
h3 {
margin-top: 0;
margin-bottom: .5rem;
font-size: 15px;
}
p {
font-size: 12px;
margin: 0;
color: var(--grey);
}
.fill-space {
flex: 1;
}
.action-row {
display: flex;
align-items: center;
flex-wrap: wrap;
justify-content: flex-end;
.goto {
background-color: white;
padding: 2px 1rem;
color: black;
border-radius: 50vw;
font-size: 12px;
margin: .5rem 0 .5rem 1rem;
white-space: nowrap;
}
}
}
} }
} }
.left {
flex-basis: 300px;
box-sizing: border-box;
padding: 1.5rem;
background: linear-gradient(40deg, rgb(80, 66, 121), rgb(177, 59, 122), rgb(225, 53, 81), rgb(230, 107, 86));
box-shadow: inset -2px 1px 15px -9px #000;
h1 {
color: white;
}
.firststeps {
color: #fad6e3;
text-transform: uppercase;
font-size: 12px;
font-weight: bold;
}
p {
color: #fad6e3;
font-size: 12px;
font-weight: bold;
}
button {
width: 100%;
}
}
.right {
padding: 1.5rem;
flex: 1;
box-sizing: border-box;
.desc {
color: var(--grey);
font-size: 20px;
margin-top: .5rem;
}
.onboarding-row {
display: flex;
padding: 1rem 0;
align-items: center;
.prev {
height: 40px;
width: 40px;
min-width: 40px;
border-radius: .5rem;
display: flex;
align-items: center;
justify-content: center;
margin-right: 2rem;
color: white;
font-size: 1.2rem;
box-shadow: 0 3px 8px 0 rgb(0 0 0 / 6%);
}
h3 {
margin-top: 0;
margin-bottom: .5rem;
font-size: 15px;
}
p {
font-size: 12px;
margin: 0;
color: var(--grey);
}
.fill-space {
flex: 1;
}
.action-row {
display: flex;
align-items: center;
flex-wrap: wrap;
justify-content: flex-end;
.goto {
background-color: white;
padding: 2px 1rem;
color: black;
border-radius: 50vw;
font-size: 12px;
margin: .5rem 0 .5rem 1rem;
white-space: nowrap;
}
}
}
}
}

View File

@ -8,7 +8,7 @@ describe('OnboardingComponent', () => {
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ OnboardingComponent ] declarations: [ OnboardingComponent ],
}) })
.compileComponents(); .compileComponents();
}); });

View File

@ -1,16 +1,19 @@
import { Component, OnInit } from '@angular/core'; import { Component } from '@angular/core';
import { AuthenticationService } from 'src/app/services/authentication.service';
@Component({ @Component({
selector: 'cnsl-onboarding', selector: 'cnsl-onboarding',
templateUrl: './onboarding.component.html', templateUrl: './onboarding.component.html',
styleUrls: ['./onboarding.component.scss'] styleUrls: ['./onboarding.component.scss'],
}) })
export class OnboardingComponent { export class OnboardingComponent {
public steps = [ public steps: Array<{
{ titleI18nKey: 'ONBOARDING.STEPS.1.TITLE', descI18nKey: 'ONBOARDING.STEPS.1.DESC', docs: "https://docs.zitadel.ch/use", link: ['/projects', 'create'] }, titleI18nKey: string;
{ titleI18nKey: 'ONBOARDING.STEPS.2.TITLE', descI18nKey: 'ONBOARDING.STEPS.2.DESC', docs: "https://docs.zitadel.ch/use", link: ['/projects'] }, descI18nKey: string,
{ titleI18nKey: 'ONBOARDING.STEPS.3.TITLE', descI18nKey: 'ONBOARDING.STEPS.3.DESC', link: ['/iam', 'policies'] }, docs?: string;
]; link?: string[];
constructor() { } }> = [
{ titleI18nKey: 'ONBOARDING.STEPS.1.TITLE', descI18nKey: 'ONBOARDING.STEPS.1.DESC', docs: 'https://docs.zitadel.ch/use', link: ['/projects', 'create'] },
{ titleI18nKey: 'ONBOARDING.STEPS.2.TITLE', descI18nKey: 'ONBOARDING.STEPS.2.DESC', docs: 'https://docs.zitadel.ch/use', link: ['/projects'] },
{ titleI18nKey: 'ONBOARDING.STEPS.3.TITLE', descI18nKey: 'ONBOARDING.STEPS.3.DESC', link: ['/iam', 'policies'] },
];
} }

View File

@ -0,0 +1,20 @@
<div class="paginator">
<div class="col">
<p class="length"><span>{{length}} </span>{{'PAGINATOR.COUNT' | translate}}</p>
<p class="ts" *ngIf="timestamp">{{timestamp | timestampToDate | localizedDate: 'EEEE dd. MMM YYYY, HH:mm'}}</p>
</div>
<span class="fill-space"></span>
<span class="pos">{{pageIndex * pageSize}} - {{pageIndex * pageSize + pageSize}} </span>
<div class="row">
<cnsl-form-field class="size" appearance="outline">
<mat-select [(ngModel)]="pageSize" (selectionChange)="emitChange()">
<mat-option *ngFor="let sizeOption of pageSizeOptions" [value]="sizeOption">
{{sizeOption}}
</mat-option>
</mat-select>
</cnsl-form-field>
<button (click)="previous()" [disabled]="!previousPossible" mat-button>{{'PAGINATOR.PREVIOUS' |
translate}}</button>
<button (click)="next()" [disabled]="!nextPossible" mat-button>{{'PAGINATOR.NEXT' | translate}}</button>
</div>
</div>

View File

@ -0,0 +1,62 @@
.paginator {
display: flex;
align-items: center;
flex-direction: column;
padding-top: .5rem;
padding-bottom: 1rem;
@media only screen and (min-width: 500px) {
flex-direction: row;
}
.col {
p {
margin: 0;
}
.length {
font-size: 14px;
}
.ts {
font-size: 12px;
color: var(--grey);
}
}
.fill-space {
flex: 1;
}
.pos {
font-size: 12px;
margin: .5rem 1rem .5rem 1rem;
color: var(--grey);
}
.size {
transform: translateY(1px);
margin-right: .5rem;
}
button {
margin: 0 .5rem;
}
:last-child {
margin-right: 0;
}
.row {
display: flex;
align-items: center;
}
}
/* stylelint-disable */
::ng-deep .mat-select {
min-width: 60px;
height: 36px !important;
padding-top: 8px !important;
}
/* stylelint-enable */

View File

@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { PaginatorComponent } from './paginator.component';
describe('PaginatorComponent', () => {
let component: PaginatorComponent;
let fixture: ComponentFixture<PaginatorComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ PaginatorComponent ],
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(PaginatorComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,57 @@
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { Timestamp } from 'src/app/proto/generated/google/protobuf/timestamp_pb';
export interface PageEvent {
length: number;
pageSize: number;
pageIndex: number;
pageSizeOptions: Array<number>;
}
@Component({
selector: 'cnsl-paginator',
templateUrl: './paginator.component.html',
styleUrls: ['./paginator.component.scss'],
})
export class PaginatorComponent {
@Input() public timestamp!: Timestamp.AsObject;
@Input() public length: number = 0;
@Input() public pageSize: number = 10;
@Input() public pageIndex: number = 0;
@Input() public pageSizeOptions: Array<number> = [10, 25, 50];
@Output() public page: EventEmitter<PageEvent> = new EventEmitter();
constructor() { }
public previous(): void {
if (this.previousPossible) {
this.pageIndex = this.pageIndex - 1;
this.emitChange();
}
}
public next(): void {
if (this.nextPossible) {
this.pageIndex = this.pageIndex + 1;
this.emitChange();
}
}
get previousPossible(): boolean {
const temp = this.pageIndex - 1;
return (temp >= 0);
}
get nextPossible(): boolean {
const temp = this.pageIndex + 1;
return (temp <= (this.length / this.pageSize));
}
public emitChange(): void {
this.page.emit({
length: this.length,
pageSize: this.pageSize,
pageIndex: this.pageIndex,
pageSizeOptions: this.pageSizeOptions,
});
}
}

View File

@ -0,0 +1,31 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatSelectModule } from '@angular/material/select';
import { TranslateModule } from '@ngx-translate/core';
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 { FormFieldModule } from '../form-field/form-field.module';
import { PaginatorComponent } from './paginator.component';
@NgModule({
declarations: [PaginatorComponent],
imports: [
CommonModule,
FormsModule,
TranslateModule,
MatButtonModule,
TimestampToDatePipeModule,
FormFieldModule,
MatSelectModule,
LocalizedDatePipeModule,
],
exports: [
PaginatorComponent,
],
})
export class PaginatorModule { }

View File

@ -24,7 +24,6 @@ export class AddIdpDialogComponent {
public idp: IDP.AsObject | undefined = undefined; public idp: IDP.AsObject | undefined = undefined;
public availableIdps: Array<IDP.AsObject[] | IDP.AsObject> | string[] = []; public availableIdps: Array<IDP.AsObject[] | IDP.AsObject> | string[] = [];
public IdpProviderType: any = IDPOwnerType;
constructor( constructor(
private mgmtService: ManagementService, private mgmtService: ManagementService,

View File

@ -18,7 +18,7 @@
} }
.info { .info {
display: block; display: block;
} }
} }
} }
@ -51,25 +51,25 @@
color: white; color: white;
.line { .line {
display: flex; display: flex;
align-items: center; align-items: center;
img { img {
height: 30px; height: 30px;
width: 30px; width: 30px;
margin-right: 1rem; margin-right: 1rem;
border-radius: .5rem; border-radius: .5rem;
object-fit: contain; object-fit: contain;
} }
div { div {
flex: 1; flex: 1;
display: block; display: block;
* { * {
display: block; display: block;
}
} }
}
} }
} }
@ -116,13 +116,13 @@
} }
&:not(.disabled) { &:not(.disabled) {
&:hover { &:hover {
.rm { .rm {
display: block; display: block;
}
} }
}
} }
img { img {
height: 100%; height: 100%;
width: 100%; width: 100%;

View File

@ -195,7 +195,7 @@ export class LoginPolicyComponent implements OnDestroy {
}); });
dialogRef.afterClosed().subscribe(resp => { dialogRef.afterClosed().subscribe(resp => {
if (resp && resp.idp) { if (resp && resp.idp && resp.type) {
this.addIdp(resp.idp, resp.type).then(() => { this.addIdp(resp.idp, resp.type).then(() => {
this.loading = true; this.loading = true;
setTimeout(() => { setTimeout(() => {
@ -208,12 +208,10 @@ export class LoginPolicyComponent implements OnDestroy {
}); });
} }
private addIdp(idp: IDP.AsObject | IDP.AsObject, ownerType?: IDPOwnerType): Promise<any> { private addIdp(idp: IDP.AsObject | IDP.AsObject, ownerType: IDPOwnerType): Promise<any> {
switch (this.serviceType) { switch (this.serviceType) {
case PolicyComponentServiceType.MGMT: case PolicyComponentServiceType.MGMT:
if (ownerType) { return (this.service as ManagementService).addIDPToLoginPolicy(idp.id, ownerType);
return (this.service as ManagementService).addIDPToLoginPolicy(idp.id, ownerType);
}
case PolicyComponentServiceType.ADMIN: case PolicyComponentServiceType.ADMIN:
return (this.service as AdminService).addIDPToLoginPolicy(idp.id); return (this.service as AdminService).addIDPToLoginPolicy(idp.id);
} }

View File

@ -30,7 +30,7 @@ import { OrgIamPolicyComponent } from './org-iam-policy.component';
InfoSectionModule, InfoSectionModule,
TranslateModule, TranslateModule,
DetailLayoutModule, DetailLayoutModule,
LinksModule LinksModule,
], ],
}) })
export class OrgIamPolicyModule { } export class OrgIamPolicyModule { }

View File

@ -19,7 +19,7 @@
padding: .3rem 0; padding: .3rem 0;
.icon { .icon {
margin-right: 1rem; margin-right: 1rem;
} }
.left-desc { .left-desc {

View File

@ -66,7 +66,8 @@ export class PasswordLockoutPolicyComponent implements OnDestroy {
}); });
} }
private getData(): Promise<AdminGetPasswordLockoutPolicyResponse.AsObject | MgmtGetPasswordLockoutPolicyResponse.AsObject> { private getData():
Promise<AdminGetPasswordLockoutPolicyResponse.AsObject | MgmtGetPasswordLockoutPolicyResponse.AsObject> {
switch (this.serviceType) { switch (this.serviceType) {
case PolicyComponentServiceType.MGMT: case PolicyComponentServiceType.MGMT:
return (this.service as ManagementService).getPasswordLockoutPolicy(); return (this.service as ManagementService).getPasswordLockoutPolicy();

View File

@ -68,14 +68,15 @@ h2 {
} }
.warn { .warn {
margin-bottom: .5rem; margin-bottom: .5rem;
} }
.icons { .icons {
margin-bottom: 1rem; margin-bottom: 1rem;
.icon {
margin-right: .5rem; .icon {
} margin-right: .5rem;
}
} }
.fill-space { .fill-space {

View File

@ -24,13 +24,13 @@ export class PolicyGridComponent implements OnInit {
constructor(private mgmtService: ManagementService, private adminService: AdminService) { } constructor(private mgmtService: ManagementService, private adminService: AdminService) { }
public ngOnInit(): void { public ngOnInit(): void {
if (this.type == PolicyGridType.ORG) { if (this.type === PolicyGridType.ORG) {
this.mgmtService.getPasswordComplexityPolicy().then((resp) => { this.mgmtService.getPasswordComplexityPolicy().then((resp) => {
if (resp.policy) { if (resp.policy) {
this.complexityPolicy = resp.policy; this.complexityPolicy = resp.policy;
} }
}); });
} else if (this.type == PolicyGridType.IAM) { } else if (this.type === PolicyGridType.IAM) {
this.adminService.getPasswordComplexityPolicy().then((resp) => { this.adminService.getPasswordComplexityPolicy().then((resp) => {
if (resp.policy) { if (resp.policy) {
this.complexityPolicy = resp.policy; this.complexityPolicy = resp.policy;

View File

@ -7,8 +7,8 @@ import { Member } from 'src/app/proto/generated/zitadel/member_pb';
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
export enum ProjectType { export enum ProjectType {
PROJECTTYPE_OWNED = "OWNED", PROJECTTYPE_OWNED = 'OWNED',
PROJECTTYPE_GRANTED = "GRANTED" PROJECTTYPE_GRANTED = 'GRANTED',
} }
/** /**
@ -35,7 +35,10 @@ export class ProjectMembersDataSource extends DataSource<Member.AsObject> {
this.loadingSubject.next(true); this.loadingSubject.next(true);
const promise: Promise<ListProjectMembersResponse.AsObject> | Promise<ListProjectGrantMembersResponse.AsObject> | undefined = const promise:
Promise<ListProjectMembersResponse.AsObject> |
Promise<ListProjectGrantMembersResponse.AsObject>
| undefined =
projectType === ProjectType.PROJECTTYPE_OWNED ? projectType === ProjectType.PROJECTTYPE_OWNED ?
this.mgmtService.listProjectMembers(projectId, pageSize, offset) : this.mgmtService.listProjectMembers(projectId, pageSize, offset) :
projectType === ProjectType.PROJECTTYPE_GRANTED && grantId ? projectType === ProjectType.PROJECTTYPE_GRANTED && grantId ?

View File

@ -111,18 +111,22 @@ export class ProjectMembersComponent {
public removeProjectMemberSelection(): void { public removeProjectMemberSelection(): void {
Promise.all(this.selection.map(member => { Promise.all(this.selection.map(member => {
if (this.projectType === ProjectType.PROJECTTYPE_OWNED) { if (this.projectType === ProjectType.PROJECTTYPE_OWNED) {
return this.mgmtService.removeProjectMember((this.project as Project.AsObject).id, member.userId).then(() => { return this.mgmtService.removeProjectMember((this.project as Project.AsObject).id, member.userId)
this.toast.showInfo('PROJECT.TOAST.MEMBERREMOVED', true); .then(() => {
}).catch(error => {
this.toast.showError(error);
});
} else if (this.projectType === ProjectType.PROJECTTYPE_GRANTED) {
return this.mgmtService.removeProjectGrantMember((this.project as GrantedProject.AsObject).projectId, this.grantId,
member.userId).then(() => {
this.toast.showInfo('PROJECT.TOAST.MEMBERREMOVED', true); this.toast.showInfo('PROJECT.TOAST.MEMBERREMOVED', true);
}).catch(error => { }).catch(error => {
this.toast.showError(error); this.toast.showError(error);
}); });
} else if (this.projectType === ProjectType.PROJECTTYPE_GRANTED) {
return this.mgmtService.removeProjectGrantMember(
(this.project as GrantedProject.AsObject).projectId,
this.grantId,
member.userId,
).then(() => {
this.toast.showInfo('PROJECT.TOAST.MEMBERREMOVED', true);
}).catch(error => {
this.toast.showError(error);
});
} }
})).then(() => { })).then(() => {
setTimeout(() => { setTimeout(() => {
@ -173,8 +177,12 @@ export class ProjectMembersComponent {
return this.mgmtService.addProjectMember((this.project as Project.AsObject).id, user.id, roles); return this.mgmtService.addProjectMember((this.project as Project.AsObject).id, user.id, roles);
} else if (this.projectType === ProjectType.PROJECTTYPE_GRANTED) { } else if (this.projectType === ProjectType.PROJECTTYPE_GRANTED) {
return this.mgmtService.addProjectGrantMember((this.project as GrantedProject.AsObject).projectId, this.grantId, return this.mgmtService.addProjectGrantMember(
user.id, roles); (this.project as GrantedProject.AsObject).projectId,
this.grantId,
user.id,
roles,
);
} }
})).then(() => { })).then(() => {
setTimeout(() => { setTimeout(() => {

View File

@ -72,8 +72,8 @@
<span>{{'PROJECT.ROLE.EMPTY' | translate}}</span> <span>{{'PROJECT.ROLE.EMPTY' | translate}}</span>
</div> </div>
<mat-paginator #paginator [length]="dataSource.totalResult" [pageSize]="50" <cnsl-paginator #paginator [timestamp]="dataSource?.viewTimestamp" [length]="dataSource.totalResult" [pageSize]="50" (page)="changePage()"
[pageSizeOptions]="[25, 50, 100, 250]"> [pageSizeOptions]="[25, 50, 100, 250]">
</mat-paginator> </cnsl-paginator>
</div> </div>
</app-refresh-table> </app-refresh-table>

View File

@ -43,16 +43,15 @@
} }
tr { tr {
outline: none; outline: none;
button {
visibility: hidden;
}
&:hover {
button { button {
visibility: hidden; visibility: visible;
}
&:hover {
button {
visibility: visible;
}
} }
} }
}

View File

@ -1,13 +1,13 @@
import { SelectionModel } from '@angular/cdk/collections'; import { SelectionModel } from '@angular/cdk/collections';
import { AfterViewInit, Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core'; import { AfterViewInit, Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { MatPaginator } from '@angular/material/paginator';
import { MatTable } from '@angular/material/table'; import { MatTable } from '@angular/material/table';
import { tap } from 'rxjs/operators'; import { tap } from 'rxjs/operators';
import { Role } from 'src/app/proto/generated/zitadel/project_pb'; import { Role } from 'src/app/proto/generated/zitadel/project_pb';
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
import { ToastService } from 'src/app/services/toast.service'; import { ToastService } from 'src/app/services/toast.service';
import { PaginatorComponent } from '../paginator/paginator.component';
import { ProjectRoleDetailComponent } from './project-role-detail/project-role-detail.component'; import { ProjectRoleDetailComponent } from './project-role-detail/project-role-detail.component';
import { ProjectRolesDataSource } from './project-roles-datasource'; import { ProjectRolesDataSource } from './project-roles-datasource';
@ -21,7 +21,7 @@ export class ProjectRolesComponent implements AfterViewInit, OnInit {
@Input() public projectId: string = ''; @Input() public projectId: string = '';
@Input() public disabled: boolean = false; @Input() public disabled: boolean = false;
@Input() public actionsVisible: boolean = false; @Input() public actionsVisible: boolean = false;
@ViewChild(MatPaginator) public paginator!: MatPaginator; @ViewChild(PaginatorComponent) public paginator!: PaginatorComponent;
@ViewChild(MatTable) public table!: MatTable<Role.AsObject>; @ViewChild(MatTable) public table!: MatTable<Role.AsObject>;
public dataSource!: ProjectRolesDataSource; public dataSource!: ProjectRolesDataSource;
public selection: SelectionModel<Role.AsObject> = new SelectionModel<Role.AsObject>(true, []); public selection: SelectionModel<Role.AsObject> = new SelectionModel<Role.AsObject>(true, []);
@ -64,6 +64,11 @@ export class ProjectRolesComponent implements AfterViewInit, OnInit {
); );
} }
public changePage(): void {
this.selection.clear();
this.loadRolesPage();
}
public isAllSelected(): boolean { public isAllSelected(): boolean {
const numSelected = this.selection.selected.length; const numSelected = this.selection.selected.length;
const numRows = this.dataSource.rolesSubject.value.length; const numRows = this.dataSource.rolesSubject.value.length;

View File

@ -6,7 +6,6 @@ import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatDialogModule } from '@angular/material/dialog'; import { MatDialogModule } from '@angular/material/dialog';
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
import { MatMenuModule } from '@angular/material/menu'; import { MatMenuModule } from '@angular/material/menu';
import { MatPaginatorModule } from '@angular/material/paginator';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatTableModule } from '@angular/material/table'; import { MatTableModule } from '@angular/material/table';
import { MatTooltipModule } from '@angular/material/tooltip'; import { MatTooltipModule } from '@angular/material/tooltip';
@ -18,6 +17,7 @@ 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 { 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 { TimestampToDatePipeModule } from 'src/app/pipes/timestamp-to-date-pipe/timestamp-to-date-pipe.module';
import { PaginatorModule } from '../paginator/paginator.module';
import { RefreshTableModule } from '../refresh-table/refresh-table.module'; import { RefreshTableModule } from '../refresh-table/refresh-table.module';
import { ProjectRoleDetailComponent } from './project-role-detail/project-role-detail.component'; import { ProjectRoleDetailComponent } from './project-role-detail/project-role-detail.component';
import { ProjectRolesComponent } from './project-roles.component'; import { ProjectRolesComponent } from './project-roles.component';
@ -30,7 +30,7 @@ import { ProjectRolesComponent } from './project-roles.component';
MatButtonModule, MatButtonModule,
HasRoleModule, HasRoleModule,
MatTableModule, MatTableModule,
MatPaginatorModule, PaginatorModule,
MatDialogModule, MatDialogModule,
InputModule, InputModule,
FormsModule, FormsModule,

View File

@ -1,12 +1,11 @@
<div class="table-header-row"> <div class="table-header-row">
<div class="col"> <div class="row" *ngIf="selection.hasValue()">
<span *ngIf="!selection.hasValue()" class="count">{{dataSize}}</span> <div>
<span *ngIf="selection.hasValue()" class="count">{{selection?.selected?.length}}</span> <span class="count">{{selection?.selected?.length}}</span>
<div class="desc"> <span class="desc" >{{'ORG_DETAIL.TABLE.SELECTION' | translate}}</span>
<span *ngIf="!selection.hasValue()">{{'ORG_DETAIL.TABLE.TOTAL' | translate}}</span> </div>
<span *ngIf="selection.hasValue()">{{'ORG_DETAIL.TABLE.SELECTION' | translate}}</span> <span class="slash">|</span>
<span *ngIf="timestamp">{{timestamp | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm'}}</span> <a (click)="selection.clear()">{{'ORG_DETAIL.TABLE.CLEAR' | translate}}</a>
</div>
</div> </div>
<span class="fill-space"></span> <span class="fill-space"></span>
<mat-spinner class="spinner" *ngIf="loading" diameter="20"></mat-spinner> <mat-spinner class="spinner" *ngIf="loading" diameter="20"></mat-spinner>

View File

@ -3,23 +3,28 @@
display: flex; display: flex;
align-items: center; align-items: center;
.col { .row {
display: flex; display: flex;
flex-direction: column;
align-items: flex-start;
.desc {
font-size: .8rem;
color: var(--grey);
margin-right: 1rem;
span {
display: block;
}
}
.count { .count {
font-size: 2rem; margin-right: .5rem;
color: var(--grey);
}
.desc {
font-size: 14px;
color: var(--grey);
}
.slash {
margin: 0 .5rem;
color: var(--grey);
}
a {
cursor: pointer;
font-size: 14px;
margin-top: 2px;
} }
} }

View File

@ -6,12 +6,12 @@ import { MatIconModule } from '@angular/material/icon';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatTooltipModule } from '@angular/material/tooltip'; import { MatTooltipModule } from '@angular/material/tooltip';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { PaginatorModule } from 'src/app/modules/paginator/paginator.module';
import { LocalizedDatePipeModule } from 'src/app/pipes/localized-date-pipe/localized-date-pipe.module'; import { LocalizedDatePipeModule } from 'src/app/pipes/localized-date-pipe/localized-date-pipe.module';
import { TimestampToDatePipeModule } from 'src/app/pipes/timestamp-to-date-pipe/timestamp-to-date-pipe.module'; import { TimestampToDatePipeModule } from 'src/app/pipes/timestamp-to-date-pipe/timestamp-to-date-pipe.module';
import { RefreshTableComponent } from './refresh-table.component'; import { RefreshTableComponent } from './refresh-table.component';
@NgModule({ @NgModule({
declarations: [RefreshTableComponent], declarations: [RefreshTableComponent],
imports: [ imports: [
@ -24,6 +24,7 @@ import { RefreshTableComponent } from './refresh-table.component';
MatProgressSpinnerModule, MatProgressSpinnerModule,
TimestampToDatePipeModule, TimestampToDatePipeModule,
LocalizedDatePipeModule, LocalizedDatePipeModule,
PaginatorModule,
], ],
exports: [ exports: [
RefreshTableComponent, RefreshTableComponent,

View File

@ -2,8 +2,8 @@
[emitRefreshOnPreviousRoutes]="refreshOnPreviousRoutes" [timestamp]="dataSource?.viewTimestamp" [emitRefreshOnPreviousRoutes]="refreshOnPreviousRoutes" [timestamp]="dataSource?.viewTimestamp"
[dataSize]="dataSource?.totalResult" [selection]="selection"> [dataSize]="dataSource?.totalResult" [selection]="selection">
<cnsl-form-field @appearfade *ngIf="userGrantListSearchKey != undefined" actions class="filtername"> <cnsl-form-field @appearfade *ngIf="userGrantListSearchKey != undefined" actions class="filtername">
<input cnslInput (keyup)="applyFilter($event)" <input class="filterinput" cnslInput (keyup)="applyFilter($event)"
[placeholder]="('USER.TABLE.FILTER.' + userGrantListSearchKey.toString()) | translate" #input> [placeholder]="('USER.GRANTS.FILTER.' + userGrantListSearchKey.toString()) | translate" #input>
</cnsl-form-field> </cnsl-form-field>
<button color="warn" matTooltip="{{'GRANTS.DELETE' | translate}}" class="icon-button" mat-icon-button actions <button color="warn" matTooltip="{{'GRANTS.DELETE' | translate}}" class="icon-button" mat-icon-button actions
@ -78,6 +78,20 @@
</td> </td>
</ng-container> </ng-container>
<ng-container matColumnDef="creationDate">
<th mat-header-cell *matHeaderCellDef> {{'PROJECT.GRANT.CREATIONDATE' | translate}} </th>
<td mat-cell *matCellDef="let grant">
<span>{{grant.details.creationDate | timestampToDate | localizedDate: 'dd. MMM, HH:mm' }}</span>
</td>
</ng-container>
<ng-container matColumnDef="changeDate">
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.GRANT.CHANGEDATE' | translate }} </th>
<td mat-cell *matCellDef="let grant">
<span>{{grant.details.changeDate | timestampToDate | localizedDate: 'dd. MMM, HH:mm' }}</span>
</td>
</ng-container>
<ng-container matColumnDef="roleNamesList"> <ng-container matColumnDef="roleNamesList">
<th mat-header-cell *matHeaderCellDef class="role-data"> <th mat-header-cell *matHeaderCellDef class="role-data">
{{ 'PROJECT.GRANT.ROLENAMESLIST' | translate }} {{ 'PROJECT.GRANT.ROLENAMESLIST' | translate }}
@ -106,7 +120,6 @@
<ng-container <ng-container
*ngIf="(context === UserGrantContext.OWNED_PROJECT || context === UserGrantContext.USER || context === UserGrantContext.NONE) && grantToEdit == grant.id && loadedProjectId && loadedProjectId === grant.projectId"> *ngIf="(context === UserGrantContext.OWNED_PROJECT || context === UserGrantContext.USER || context === UserGrantContext.NONE) && grantToEdit == grant.id && loadedProjectId && loadedProjectId === grant.projectId">
<cnsl-form-field class="form-field" appearance="outline"> <cnsl-form-field class="form-field" appearance="outline">
<!-- <cnsl-label>{{ 'PROJECT.GRANT.ROLENAMESLIST' | translate }}</cnsl-label> -->
<mat-select [(ngModel)]="grant.roleKeysList" multiple <mat-select [(ngModel)]="grant.roleKeysList" multiple
[disabled]="disableWrite || !((['user.grant.write$'] | hasRole | async) || ((context === UserGrantContext.OWNED_PROJECT ? ['user.grant.write:' + grant?.projectId] : context === UserGrantContext.GRANTED_PROJECT ? ['user.grant.write:' + grant?.id] : []) | hasRole | async))" [disabled]="disableWrite || !((['user.grant.write$'] | hasRole | async) || ((context === UserGrantContext.OWNED_PROJECT ? ['user.grant.write:' + grant?.projectId] : context === UserGrantContext.GRANTED_PROJECT ? ['user.grant.write:' + grant?.id] : []) | hasRole | async))"
(selectionChange)="updateRoles(grant, $event)"> (selectionChange)="updateRoles(grant, $event)">
@ -125,7 +138,6 @@
<ng-container <ng-container
*ngIf="(context === UserGrantContext.GRANTED_PROJECT || context === UserGrantContext.USER || context === UserGrantContext.NONE) && loadedId && loadedId === grant.id && grantToEdit == grant.id"> *ngIf="(context === UserGrantContext.GRANTED_PROJECT || context === UserGrantContext.USER || context === UserGrantContext.NONE) && loadedId && loadedId === grant.id && grantToEdit == grant.id">
<cnsl-form-field class="form-field" appearance="outline"> <cnsl-form-field class="form-field" appearance="outline">
<!-- <cnsl-label>{{ 'PROJECT.GRANT.ROLENAMESLIST' | translate }}</cnsl-label> -->
<mat-select [(ngModel)]="grant.roleKeysList" multiple <mat-select [(ngModel)]="grant.roleKeysList" multiple
[disabled]="disableWrite || !((['user.grant.write$'] | hasRole | async) || ((context === UserGrantContext.OWNED_PROJECT ? ['user.grant.write:' + grant?.projectId] : context === UserGrantContext.GRANTED_PROJECT ? ['user.grant.write:' + grant?.id] : []) | hasRole | async))" [disabled]="disableWrite || !((['user.grant.write$'] | hasRole | async) || ((context === UserGrantContext.OWNED_PROJECT ? ['user.grant.write:' + grant?.projectId] : context === UserGrantContext.GRANTED_PROJECT ? ['user.grant.write:' + grant?.id] : []) | hasRole | async))"
(selectionChange)="updateRoles(grant, $event)"> (selectionChange)="updateRoles(grant, $event)">
@ -153,9 +165,9 @@
<i class="las la-exclamation"></i> <i class="las la-exclamation"></i>
<span>{{'GRANTS.EMPTY' | translate}}</span> <span>{{'GRANTS.EMPTY' | translate}}</span>
</div> </div>
<mat-paginator class="paginator" #paginator [length]="dataSource.totalResult" [pageSize]="INITIAL_PAGE_SIZE" <cnsl-paginator class="paginator" #paginator [timestamp]="dataSource?.viewTimestamp" [length]="dataSource.totalResult" [pageSize]="INITIAL_PAGE_SIZE"
[length]="dataSource.totalResult" [pageSizeOptions]="[2, 3, 25, 50, 100, 250]" (page)="changePage($event)"> [length]="dataSource.totalResult" [pageSizeOptions]="[2, 3, 25, 50, 100, 250]" (page)="changePage($event)">
</mat-paginator> </cnsl-paginator>
</div> </div>
</app-refresh-table> </app-refresh-table>

View File

@ -109,5 +109,11 @@
} }
.filtername { .filtername {
flex: 1;
margin-right: 1rem; margin-right: 1rem;
.filterinput {
height: 36px;
transform: translateY(1px);
}
} }

View File

@ -1,7 +1,6 @@
import { SelectionModel } from '@angular/cdk/collections'; import { SelectionModel } from '@angular/cdk/collections';
import { AfterViewInit, Component, Input, OnInit, ViewChild } from '@angular/core'; import { AfterViewInit, Component, Input, OnInit, ViewChild } from '@angular/core';
import { MatInput } from '@angular/material/input'; import { MatInput } from '@angular/material/input';
import { MatPaginator, PageEvent } from '@angular/material/paginator';
import { MatSelectChange } from '@angular/material/select'; import { MatSelectChange } from '@angular/material/select';
import { MatTable } from '@angular/material/table'; import { MatTable } from '@angular/material/table';
import { tap } from 'rxjs/operators'; import { tap } from 'rxjs/operators';
@ -19,6 +18,7 @@ import {
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
import { ToastService } from 'src/app/services/toast.service'; import { ToastService } from 'src/app/services/toast.service';
import { PageEvent, PaginatorComponent } from '../paginator/paginator.component';
import { UserGrantContext, UserGrantsDataSource } from './user-grants-datasource'; import { UserGrantContext, UserGrantsDataSource } from './user-grants-datasource';
export enum UserGrantListSearchKey { export enum UserGrantListSearchKey {
@ -45,7 +45,7 @@ export class UserGrantsComponent implements OnInit, AfterViewInit {
public dataSource!: UserGrantsDataSource; public dataSource!: UserGrantsDataSource;
public selection: SelectionModel<UserGrant.AsObject> = new SelectionModel<UserGrant.AsObject>(true, []); public selection: SelectionModel<UserGrant.AsObject> = new SelectionModel<UserGrant.AsObject>(true, []);
@ViewChild(MatPaginator) public paginator!: MatPaginator; @ViewChild(PaginatorComponent) public paginator!: PaginatorComponent;
@ViewChild(MatTable) public table!: MatTable<UserGrant.AsObject>; @ViewChild(MatTable) public table!: MatTable<UserGrant.AsObject>;
@Input() disableWrite: boolean = false; @Input() disableWrite: boolean = false;
@ -75,7 +75,7 @@ export class UserGrantsComponent implements OnInit, AfterViewInit {
@Input() public displayedColumns: string[] = ['select', @Input() public displayedColumns: string[] = ['select',
'user', 'user',
'org', 'org',
'projectId', 'dates', 'roleNamesList']; 'projectId', 'creationDate', 'changeDate', 'roleNamesList'];
public ngOnInit(): void { public ngOnInit(): void {
this.dataSource = new UserGrantsDataSource(this.userService); this.dataSource = new UserGrantsDataSource(this.userService);

View File

@ -4,7 +4,6 @@ import { FormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { MatCheckboxModule } from '@angular/material/checkbox'; import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
import { MatPaginatorModule } from '@angular/material/paginator';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatSelectModule } from '@angular/material/select'; import { MatSelectModule } from '@angular/material/select';
import { MatTableModule } from '@angular/material/table'; import { MatTableModule } from '@angular/material/table';
@ -18,6 +17,7 @@ import { TimestampToDatePipeModule } from 'src/app/pipes/timestamp-to-date-pipe/
import { InputModule } from '../../modules/input/input.module'; import { InputModule } from '../../modules/input/input.module';
import { AvatarModule } from '../avatar/avatar.module'; import { AvatarModule } from '../avatar/avatar.module';
import { PaginatorModule } from '../paginator/paginator.module';
import { RefreshTableModule } from '../refresh-table/refresh-table.module'; import { RefreshTableModule } from '../refresh-table/refresh-table.module';
import { UserGrantsComponent } from './user-grants.component'; import { UserGrantsComponent } from './user-grants.component';
@ -31,7 +31,7 @@ import { UserGrantsComponent } from './user-grants.component';
MatButtonModule, MatButtonModule,
HasRoleModule, HasRoleModule,
MatTableModule, MatTableModule,
MatPaginatorModule, PaginatorModule,
MatIconModule, MatIconModule,
RouterModule, RouterModule,
MatProgressSpinnerModule, MatProgressSpinnerModule,

View File

@ -1,7 +1,7 @@
<div class="max-width-container"> <div class="enlarged-container">
<h1>{{ 'GRANTS.TITLE' | translate }}</h1> <h1>{{ 'GRANTS.TITLE' | translate }}</h1>
<p class="desc">{{'GRANTS.DESC' | translate }}</p> <p class="desc">{{'GRANTS.DESC' | translate }}</p>
<app-user-grants [displayedColumns]="['select', 'user', 'org', 'projectId', 'dates', 'roleNamesList']" <app-user-grants [displayedColumns]="['select', 'user', 'org', 'projectId', 'creationDate','changeDate', 'roleNamesList']"
[disableWrite]="((['user.grant.write$'] | hasRole) | async) == false" [disableWrite]="((['user.grant.write$'] | hasRole) | async) == false"
[disableDelete]="((['user.grant.delete$'] | hasRole) | async) == false" [disableDelete]="((['user.grant.delete$'] | hasRole) | async) == false"
[refreshOnPreviousRoutes]="['/grant-create']"> [refreshOnPreviousRoutes]="['/grant-create']">

View File

@ -34,74 +34,74 @@
.onboard, .onboard,
.quickstart { .quickstart {
text-decoration: none; text-decoration: none;
cursor: pointer; cursor: pointer;
box-sizing: border-box; box-sizing: border-box;
flex: 1 0 45%; flex: 1 0 45%;
position: relative; position: relative;
border-radius: 0.5rem; border-radius: .5rem;
margin: 1rem; margin: 1rem;
padding: 1.5rem; padding: 1.5rem;
box-shadow: 0 3px 8px 0 rgb(0 0 0 / 6%); box-shadow: 0 3px 8px 0 rgb(0 0 0 / 6%);
h2 { h2 {
color: white; color: white;
} }
.first-steps { .first-steps {
text-transform: uppercase; text-transform: uppercase;
font-size: 12px; font-size: 12px;
font-weight: bold; font-weight: bold;
}
.close {
visibility: hidden;
position: absolute;
top: 0;
right: 0;
i {
color: white;
font-size: 1rem;
} }
}
&:hover {
.close { .close {
visibility: hidden; visibility: visible;
position: absolute;
top: 0;
right: 0;
i {
color: white;
font-size: 1rem;
}
}
&:hover {
.close {
visibility: visible;
}
} }
}
} }
.onboard { .onboard {
background: linear-gradient(40deg, rgb(80, 66, 121),rgb(177, 59, 122),rgb(225,53,81), rgb(230,107,86)); background: linear-gradient(40deg, rgb(80, 66, 121), rgb(177, 59, 122), rgb(225, 53, 81), rgb(230, 107, 86));
p { p {
color: #fad6e3; color: #fad6e3;
} }
} }
.quickstart { .quickstart {
background: linear-gradient(30deg, #2283a6,#6c8f59); background: linear-gradient(30deg, #2283a6, #6c8f59);
p { p {
color: #d6f3fa; color: #d6f3fa;
} }
.logo-cloud { .logo-cloud {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
margin: -0.5rem; margin: -.5rem;
i { i {
font-size: 40px; font-size: 40px;
padding: .5rem; padding: .5rem;
border: 1px solid #ffffff50; border: 1px solid #ffffff50;
border-radius: .5rem; border-radius: .5rem;
margin: 0.5rem; margin: .5rem;
color: white; color: white;
}
} }
}
} }
.item { .item {

View File

@ -15,8 +15,8 @@ export class HomeComponent {
const theme = localStorage.getItem('theme'); const theme = localStorage.getItem('theme');
this.dark = theme === 'dark-theme' ? true : theme === 'light-theme' ? false : true; this.dark = theme === 'dark-theme' ? true : theme === 'light-theme' ? false : true;
this.firstStepsDismissed = localStorage.getItem('firstStartDismissed') == 'true' ? true : false; this.firstStepsDismissed = localStorage.getItem('firstStartDismissed') === 'true' ? true : false;
this.quickstartsDismissed = localStorage.getItem('quickstartsDismissed') == 'true' ? true : false; this.quickstartsDismissed = localStorage.getItem('quickstartsDismissed') === 'true' ? true : false;
} }
dismissFirstSteps(event: Event): void { dismissFirstSteps(event: Event): void {

View File

@ -1,4 +1,4 @@
<div class="max-width-container"> <div class="enlarged-container">
<h1>{{ 'IAM.EVENTSTORE.TITLE' | translate }}</h1> <h1>{{ 'IAM.EVENTSTORE.TITLE' | translate }}</h1>
<p class="desc">{{'IAM.EVENTSTORE.DESCRIPTION' | translate }}</p> <p class="desc">{{'IAM.EVENTSTORE.DESCRIPTION' | translate }}</p>
<app-card title="{{ 'IAM.VIEWS.TITLE' | translate }}" description="{{ 'IAM.VIEWS.DESCRIPTION' | translate }}"> <app-card title="{{ 'IAM.VIEWS.TITLE' | translate }}" description="{{ 'IAM.VIEWS.DESCRIPTION' | translate }}">

View File

@ -39,7 +39,7 @@ const routes: Routes = [
// canActivate: [RoleGuard], // canActivate: [RoleGuard],
data: { data: {
roles: ['iam.features.read'], roles: ['iam.features.read'],
serviceType: FeatureServiceType.ADMIN serviceType: FeatureServiceType.ADMIN,
}, },
}, },
{ {

View File

@ -26,16 +26,17 @@
</ng-container> </ng-container>
<ng-container matColumnDef="lastSuccessfulSpoolerRun"> <ng-container matColumnDef="lastSuccessfulSpoolerRun">
<th mat-header-cell *matHeaderCellDef> {{ 'IAM.VIEWS.LASTSPOOL' | translate }} </th> <th mat-header-cell *matHeaderCellDef> {{ 'IAM.VIEWS.LASTSPOOL' | translate }} </th>
<td mat-cell *matCellDef="let view"> <td mat-cell *matCellDef="let view">
<span>{{view?.lastSuccessfulSpoolerRun | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm' }}</span> <span>{{view?.lastSuccessfulSpoolerRun | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm'
</td> }}</span>
</td>
</ng-container> </ng-container>
<ng-container matColumnDef="actions" stickyEnd> <ng-container matColumnDef="actions" stickyEnd>
<th mat-header-cell *matHeaderCellDef></th> <th mat-header-cell *matHeaderCellDef></th>
<td mat-cell *matCellDef="let view"> <td mat-cell *matCellDef="let view">
<button mat-icon-button matTooltip="{{'IAM.VIEWS.CLEAR' | translate}}" <button mat-icon-button matTooltip="{{'IAM.VIEWS.CLEAR' | translate}}" color="warn"
(click)="cancelView(view.viewName, view.database)"> (click)="cancelView(view.viewName, view.database)">
<mat-icon svgIcon="mdi_broom"></mat-icon> <mat-icon svgIcon="mdi_broom"></mat-icon>
</button> </button>
@ -48,4 +49,4 @@
<mat-paginator class="paginator" [pageSize]="10" #paginator [pageSizeOptions]="[10, 20, 100, 250]"> <mat-paginator class="paginator" [pageSize]="10" #paginator [pageSizeOptions]="[10, 20, 100, 250]">
</mat-paginator> </mat-paginator>
</app-refresh-table> </app-refresh-table>
</div> </div>

View File

@ -6,7 +6,7 @@
<h2>{{'FEATURES.TITLE' | translate}}</h2> <h2>{{'FEATURES.TITLE' | translate}}</h2>
<p class="top-desc">{{'FEATURES.DESCRIPTION' | translate}}</p> <p class="top-desc">{{'FEATURES.DESCRIPTION' | translate}}</p>
<div *ngIf="features" class="tier"> <div *ngIf="features" class="tier">
<mat-icon>stars</mat-icon> <mat-icon class="icon">stars</mat-icon>
<div class="text" *ngIf="features.tier"> <div class="text" *ngIf="features.tier">
<p class="title"><strong>ZITADEL {{features.tier.name}}</strong></p> <p class="title"><strong>ZITADEL {{features.tier.name}}</strong></p>
<p>{{features.tier?.description}}</p> <p>{{features.tier?.description}}</p>

View File

@ -13,46 +13,48 @@
} }
h2 { h2 {
font-size: 1.2rem; font-size: 1.2rem;
letter-spacing: .05em; letter-spacing: .05em;
text-transform: uppercase; text-transform: uppercase;
margin-top: 2rem; margin-top: 2rem;
} }
.tier { .tier {
display: flex;
flex-direction: row;
padding: 1rem;
border-radius: .5rem;
color: white;
background-color: rgb(245, 203, 99);
margin-bottom: .5rem;
.ext {
margin-right: .5rem;
align-self: center;
color: inherit;
}
.icon {
margin-right: 1rem;
font-size: 2rem;
}
.text {
display: flex; display: flex;
flex-direction: row; flex-direction: column;
padding: 1rem;
border-radius: .5rem;
color: white;
background-color: rgb(245, 203, 99);
margin-bottom: .5rem;
.ext { p {
margin-right: .5rem; margin: 0;
align-self: center; font-size: 14px;
color: inherit;
} }
mat-icon { .title {
margin-right: 1rem; font-size: 16px;
font-size: 2rem; margin-bottom: .5rem;
} }
}
.text { .fill-space {
display: flex; flex: 1;
flex-direction: column; }
p { }
margin: 0;
font-size: 14px;
}
.title {
font-size: 16px;
margin-bottom: .5rem;
}
}
.fill-space {
flex: 1;
}
}

View File

@ -33,7 +33,7 @@
<h2>{{'FEATURES.TITLE' | translate}}</h2> <h2>{{'FEATURES.TITLE' | translate}}</h2>
<p class="top-desc">{{'FEATURES.DESCRIPTION' | translate}}</p> <p class="top-desc">{{'FEATURES.DESCRIPTION' | translate}}</p>
<div *ngIf="features" class="tier"> <div *ngIf="features" class="tier">
<mat-icon>stars</mat-icon> <mat-icon class="icon">stars</mat-icon>
<div class="text" *ngIf="features.tier"> <div class="text" *ngIf="features.tier">
<p class="title"><strong>ZITADEL {{features.tier.name}}</strong></p> <p class="title"><strong>ZITADEL {{features.tier.name}}</strong></p>
<p>{{features.tier?.description}}</p> <p>{{features.tier?.description}}</p>

View File

@ -8,7 +8,7 @@ h2 {
text-transform: uppercase; text-transform: uppercase;
margin-top: 2rem; margin-top: 2rem;
} }
.top-desc { .top-desc {
color: var(--grey); color: var(--grey);
font-size: 14px; font-size: 14px;
@ -20,43 +20,43 @@ h2 {
} }
.tier { .tier {
display: flex;
flex-direction: row;
padding: 1rem;
border-radius: .5rem;
color: white;
background-color: rgb(245, 203, 99);
margin-bottom: .5rem;
.ext {
margin-right: .5rem;
align-self: center;
color: inherit;
}
.icon {
margin-right: 1rem;
font-size: 2rem;
}
.text {
display: flex; display: flex;
flex-direction: row; flex-direction: column;
padding: 1rem;
border-radius: .5rem;
color: white;
background-color: rgb(245, 203, 99);
margin-bottom: .5rem;
.ext { p {
margin-right: .5rem; margin: 0;
align-self: center; font-size: 14px;
color: inherit;
} }
mat-icon { .title {
margin-right: 1rem; font-size: 16px;
font-size: 2rem; margin-bottom: .5rem;
} }
}
.text { .fill-space {
display: flex; flex: 1;
flex-direction: column; }
p {
margin: 0;
font-size: 14px;
}
.title {
font-size: 16px;
margin-bottom: .5rem;
}
}
.fill-space {
flex: 1;
}
} }
.domain { .domain {

View File

@ -1,4 +1,4 @@
<div class="max-width-container"> <div class="enlarged-container">
<h1>{{ 'ORG.PAGES.LIST' | translate }}</h1> <h1>{{ 'ORG.PAGES.LIST' | translate }}</h1>
<p class="top-desc">{{'ORG.PAGES.LISTDESCRIPTION' | translate}}</p> <p class="top-desc">{{'ORG.PAGES.LISTDESCRIPTION' | translate}}</p>

View File

@ -23,6 +23,10 @@ h1 {
padding-right: 0; padding-right: 0;
} }
} }
td {
cursor: pointer;
}
} }
.pointer { .pointer {

View File

@ -11,7 +11,7 @@ import { Org, OrgNameQuery, OrgQuery } from 'src/app/proto/generated/zitadel/org
import { GrpcAuthService } from 'src/app/services/grpc-auth.service'; import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
enum OrgListSearchKey { enum OrgListSearchKey {
NAME = "NAME", NAME = 'NAME',
} }
@Component({ @Component({
@ -53,7 +53,7 @@ export class OrgListComponent implements AfterViewInit {
this.loadingSubject.next(true); this.loadingSubject.next(true);
let query; let query;
if (filter) { if (filter) {
const query = new OrgQuery(); query = new OrgQuery();
const orgNameQuery = new OrgNameQuery(); const orgNameQuery = new OrgNameQuery();
orgNameQuery.setMethod(TextQueryMethod.TEXT_QUERY_METHOD_CONTAINS_IGNORE_CASE); orgNameQuery.setMethod(TextQueryMethod.TEXT_QUERY_METHOD_CONTAINS_IGNORE_CASE);
orgNameQuery.setName(filter); orgNameQuery.setName(filter);

View File

@ -51,7 +51,7 @@ p.desc {
margin: 0 -1.5rem; margin: 0 -1.5rem;
.formfield { .formfield {
max-width: 400px; max-width: 400px;
} }
.step-title { .step-title {
@ -126,4 +126,4 @@ p.desc {
padding: .5rem 4rem; padding: .5rem 4rem;
float: right; float: right;
} }
} }

View File

@ -78,11 +78,17 @@ export class AppCreateComponent implements OnInit, OnDestroy {
]; ];
// set to oidc first // set to oidc first
public authMethodTypes: { type: OIDCAuthMethodType | APIAuthMethodType, checked: boolean, disabled: boolean; api?: boolean; oidc?: boolean; }[] = [ public authMethodTypes: {
{ type: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_BASIC, checked: false, disabled: false, oidc: true }, type: OIDCAuthMethodType | APIAuthMethodType,
{ type: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE, checked: false, disabled: false, oidc: true }, checked: boolean,
{ type: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_POST, checked: false, disabled: false, oidc: true }, disabled: boolean;
]; api?: boolean;
oidc?: boolean;
}[] = [
{ type: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_BASIC, checked: false, disabled: false, oidc: true },
{ type: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE, checked: false, disabled: false, oidc: true },
{ type: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_POST, checked: false, disabled: false, oidc: true },
];
// stepper // stepper
firstFormGroup!: FormGroup; firstFormGroup!: FormGroup;
@ -196,11 +202,18 @@ export class AppCreateComponent implements OnInit, OnDestroy {
const partialConfig = getPartialConfigFromAuthMethod(form.authMethod); const partialConfig = getPartialConfigFromAuthMethod(form.authMethod);
if (this.isStepperOIDC && partialConfig && partialConfig.oidc) { if (this.isStepperOIDC && partialConfig && partialConfig.oidc) {
this.oidcAppRequest.responseTypesList = partialConfig.oidc?.responseTypesList ?? []; this.oidcAppRequest.responseTypesList = partialConfig.oidc?.responseTypesList
this.oidcAppRequest.grantTypesList = partialConfig.oidc?.grantTypesList ?? []; ?? [];
this.oidcAppRequest.authMethodType = partialConfig.oidc?.authMethodType ?? OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE;
this.oidcAppRequest.grantTypesList = partialConfig.oidc?.grantTypesList
?? [];
this.oidcAppRequest.authMethodType = partialConfig.oidc?.authMethodType
?? OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE;
} else if (this.isStepperAPI && partialConfig && partialConfig.api) { } else if (this.isStepperAPI && partialConfig && partialConfig.api) {
this.apiAppRequest.authMethodType = partialConfig.api?.authMethodType ?? APIAuthMethodType.API_AUTH_METHOD_TYPE_BASIC; this.apiAppRequest.authMethodType = partialConfig.api?.authMethodType
?? APIAuthMethodType.API_AUTH_METHOD_TYPE_BASIC;
} }
}); });
} }
@ -265,10 +278,10 @@ export class AppCreateComponent implements OnInit, OnDestroy {
this.form.updateValueAndValidity(); this.form.updateValueAndValidity();
} }
public changeStep(event: StepperSelectionEvent) { public changeStep(event: StepperSelectionEvent): void {
if (event.selectedIndex >= 2) { if (event.selectedIndex >= 2) {
this.requestRedirectValuesSubject$.next(); this.requestRedirectValuesSubject$.next();
}; }
} }
private async getData({ projectid }: Params): Promise<void> { private async getData({ projectid }: Params): Promise<void> {
@ -335,8 +348,8 @@ export class AppCreateComponent implements OnInit, OnDestroy {
const dialogRef = this.dialog.open(AppSecretDialogComponent, { const dialogRef = this.dialog.open(AppSecretDialogComponent, {
data: { data: {
clientSecret: clientSecret, clientSecret: clientSecret,
clientId: clientId clientId: clientId,
} },
}); });
dialogRef.afterClosed().subscribe(() => { dialogRef.afterClosed().subscribe(() => {
@ -382,19 +395,19 @@ export class AppCreateComponent implements OnInit, OnDestroy {
} }
get isDevOIDC(): boolean { get isDevOIDC(): boolean {
return (this.formappType?.value as RadioItemAppType).createType == AppCreateType.OIDC; return (this.formappType?.value as RadioItemAppType).createType === AppCreateType.OIDC;
} }
get isStepperOIDC(): boolean { get isStepperOIDC(): boolean {
return (this.appType?.value as RadioItemAppType).createType == AppCreateType.OIDC; return (this.appType?.value as RadioItemAppType).createType === AppCreateType.OIDC;
} }
get isDevAPI(): boolean { get isDevAPI(): boolean {
return (this.formappType?.value as RadioItemAppType).createType == AppCreateType.API; return (this.formappType?.value as RadioItemAppType).createType === AppCreateType.API;
} }
get isStepperAPI(): boolean { get isStepperAPI(): boolean {
return (this.appType?.value as RadioItemAppType).createType == AppCreateType.API; return (this.appType?.value as RadioItemAppType).createType === AppCreateType.API;
} }
}; }

View File

@ -14,7 +14,7 @@
<ng-template appHasRole [appHasRole]="['project.app.write:'+projectId, 'project.app.write']"> <ng-template appHasRole [appHasRole]="['project.app.write:'+projectId, 'project.app.write']">
<button *ngIf="!editState" matTooltip="{{'ACTIONS.EDIT' | translate}}" mat-icon-button <button *ngIf="!editState" matTooltip="{{'ACTIONS.EDIT' | translate}}" mat-icon-button
(click)="editState = !editState" aria-label="edit app name"> (click)="editState = !editState" aria-label="edit app name">
<i class="las la-edit"></i> <i class="las la-edit"></i>
</button> </button>
<button *ngIf="editState" (click)="saveApp()" [disabled]="appNameForm.invalid || name?.disabled" <button *ngIf="editState" (click)="saveApp()" [disabled]="appNameForm.invalid || name?.disabled"
@ -22,22 +22,24 @@
<i class="las la-save"></i> <i class="las la-save"></i>
</button> </button>
<ng-template appHasRole [appHasRole]="['project.app.delete:'+projectId, 'project.app.delete']">
<button matTooltip="{{'ACTIONS.DELETE' | translate}}" color="warn" mat-icon-button
(click)="deleteApp()" aria-label="delete app">
<i class="las la-trash"></i>
</button>
</ng-template>
<span class="fill-space"></span>
<button class="state-button" mat-stroked-button color="warn" <button class="state-button" mat-stroked-button color="warn"
*ngIf="app.state !== undefined && app?.state !== AppState.APP_STATE_INACTIVE" *ngIf="app && app.state !== undefined && app?.state !== AppState.APP_STATE_INACTIVE"
(click)="changeState(AppState.APP_STATE_INACTIVE)"> (click)="changeState(AppState.APP_STATE_INACTIVE)">
{{'ACTIONS.DEACTIVATE' | translate}} {{'ACTIONS.DEACTIVATE' | translate}}
</button> </button>
<button class="state-button" mat-stroked-button <button class="state-button" mat-stroked-button
*ngIf="app.state !== undefined && app?.state !== AppState.APP_STATE_ACTIVE" *ngIf="app && app.state !== undefined && app?.state !== AppState.APP_STATE_ACTIVE"
(click)="changeState(AppState.APP_STATE_ACTIVE)"> (click)="changeState(AppState.APP_STATE_ACTIVE)">
{{'ACTIONS.REACTIVATE' | translate}} {{'ACTIONS.REACTIVATE' | translate}}
</button>
</ng-template>
<ng-template appHasRole [appHasRole]="['project.app.delete:'+projectId, 'project.app.delete']">
<button matTooltip="{{'ACTIONS.DELETE' | translate}}" color="warn" mat-icon-button
(click)="deleteApp()" aria-label="delete app">
<i class="las la-trash"></i>
</button> </button>
</ng-template> </ng-template>
</ng-container> </ng-container>
@ -112,7 +114,7 @@
</cnsl-info-section> </cnsl-info-section>
<cnsl-info-section class="step-description" <cnsl-info-section class="step-description"
*ngIf="OIDCAppType?.value == OIDCAppType.OIDC_APP_TYPE_WEB || appType?.value == OIDCAppType.OIDC_APP_TYPE_USER_AGENT"> *ngIf="appType?.value == OIDCAppType.OIDC_APP_TYPE_WEB || appType?.value == OIDCAppType.OIDC_APP_TYPE_USER_AGENT">
{{'APP.OIDC.REDIRECTDESCRIPTIONWEB' | translate}} {{'APP.OIDC.REDIRECTDESCRIPTIONWEB' | translate}}
</cnsl-info-section> </cnsl-info-section>
@ -142,7 +144,7 @@
<app-client-keys [projectId]="projectId" [appId]="app.id"></app-client-keys> <app-client-keys [projectId]="projectId" [appId]="app.id"></app-client-keys>
</app-card> </app-card>
<div *ngIf="oidcForm && app?.apiConfig" class="btn-container"> <div *ngIf="apiForm && app?.apiConfig" class="btn-container">
<button class="submit-button" (click)="saveAPIApp()" color="primary" <button class="submit-button" (click)="saveAPIApp()" color="primary"
[disabled]="apiForm.invalid || !canWrite" mat-raised-button> [disabled]="apiForm.invalid || !canWrite" mat-raised-button>
{{ 'ACTIONS.SAVE' | translate }} {{ 'ACTIONS.SAVE' | translate }}

View File

@ -11,20 +11,23 @@
.title-col { .title-col {
margin-left: 2rem; margin-left: 2rem;
margin-right: 1rem; margin-right: 1rem;
min-width: 200px;
h1 { h1 {
font-size: 1.2rem; font-size: 1.2rem;
margin: 0 0 0 0; margin: 0 0 0 0;
font-weight: normal; font-weight: normal;
} }
span { span {
font-size: 12px; font-size: 12px;
color: var(--grey); color: var(--grey);
} }
} }
.fill-space {
flex: 1;
}
.desc { .desc {
width: 100%; width: 100%;
display: block; display: block;
@ -39,7 +42,7 @@
} }
.state-button { .state-button {
margin: 0 .5rem; margin-left: .5rem;
} }
} }
@ -49,43 +52,44 @@
} }
.environment-wrapper { .environment-wrapper {
padding: 1rem 0; padding: 1rem 0;
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
.environment { .environment {
min-width: 300px; min-width: 300px;
.key { .key {
font-size: 12px; font-size: 12px;
color: var(--grey); color: var(--grey);
}
.environment-row {
display: flex;
align-items: center;
overflow: hidden;
height: 30px;
button {
transition: opacity .15s ease-in-out;
visibility: hidden;
opacity: 0;
&[disabled] {
visibility: visible;
color: white;
opacity: 1;
}
}
&:hover {
button {
visibility: visible;
opacity: 1;
}
}
}
} }
.environment-row {
display: flex;
align-items: center;
overflow: hidden;
height: 30px;
button {
transition: opacity .15s ease-in-out;
visibility: hidden;
opacity: 0;
&[disabled] {
visibility: visible;
color: white;
opacity: 1;
}
}
&:hover {
button {
visibility: visible;
opacity: 1;
}
}
}
}
} }
.compliance .problem { .compliance .problem {
@ -147,8 +151,8 @@
} }
.redirect-section { .redirect-section {
flex: 1; flex: 1;
margin: 0 .5rem; margin: 0 .5rem;
} }
.formfield { .formfield {
@ -159,9 +163,9 @@
flex-basis: 100%; flex-basis: 100%;
} }
} }
.section-title { .section-title {
margin: 1.5rem 0 0 0; margin: 1.5rem 0 0 0;
} }
.full-width { .full-width {

View File

@ -192,7 +192,7 @@ export class AppDetailComponent implements OnInit, OnDestroy {
}, { }, {
i18nTitle: 'APP.PAGES.NEXTSTEPS.2.TITLE', i18nTitle: 'APP.PAGES.NEXTSTEPS.2.TITLE',
i18nDesc: 'APP.PAGES.NEXTSTEPS.2.DESC', i18nDesc: 'APP.PAGES.NEXTSTEPS.2.DESC',
href: 'https://docs.zitadel.ch' href: 'https://docs.zitadel.ch',
}, },
]; ];
} }
@ -205,91 +205,94 @@ export class AppDetailComponent implements OnInit, OnDestroy {
this.mgmtService.getIAM().then(iam => { this.mgmtService.getIAM().then(iam => {
this.isZitadel = iam.iamProjectId === this.projectId; this.isZitadel = iam.iamProjectId === this.projectId;
}); });
this.authService.isAllowed(['project.app.write$', 'project.app.write:' + projectid]).pipe(take(1)).subscribe((allowed) => { this.authService.isAllowed(['project.app.write$', 'project.app.write:' + projectid])
this.canWrite = allowed; .pipe(take(1))
this.mgmtService.getAppByID(projectid, id).then(app => { .subscribe((allowed) => {
if (app.app) { this.canWrite = allowed;
this.app = app.app; this.mgmtService.getAppByID(projectid, id).then(app => {
this.appNameForm.patchValue(this.app); if (app.app) {
this.app = app.app;
this.appNameForm.patchValue(this.app);
if (this.app.oidcConfig) { if (this.app.oidcConfig) {
this.getAuthMethodOptions('OIDC'); this.getAuthMethodOptions('OIDC');
this.initialAuthMethod = this.authMethodFromPartialConfig({ oidc: this.app.oidcConfig }); this.initialAuthMethod = this.authMethodFromPartialConfig({ oidc: this.app.oidcConfig });
this.currentAuthMethod = this.initialAuthMethod; this.currentAuthMethod = this.initialAuthMethod;
if (this.initialAuthMethod === CUSTOM_METHOD.key) { if (this.initialAuthMethod === CUSTOM_METHOD.key) {
if (!this.authMethods.includes(CUSTOM_METHOD)) { if (!this.authMethods.includes(CUSTOM_METHOD)) {
this.authMethods.push(CUSTOM_METHOD); this.authMethods.push(CUSTOM_METHOD);
}
} else {
this.authMethods = this.authMethods.filter(element => element !== CUSTOM_METHOD);
} }
} else { } else if (this.app.apiConfig) {
this.authMethods = this.authMethods.filter(element => element != CUSTOM_METHOD); this.getAuthMethodOptions('API');
}
} else if (this.app.apiConfig) {
this.getAuthMethodOptions('API');
this.initialAuthMethod = this.authMethodFromPartialConfig({ api: this.app.apiConfig }); this.initialAuthMethod = this.authMethodFromPartialConfig({ api: this.app.apiConfig });
this.currentAuthMethod = this.initialAuthMethod; this.currentAuthMethod = this.initialAuthMethod;
if (this.initialAuthMethod === CUSTOM_METHOD.key) { if (this.initialAuthMethod === CUSTOM_METHOD.key) {
if (!this.authMethods.includes(CUSTOM_METHOD)) { if (!this.authMethods.includes(CUSTOM_METHOD)) {
this.authMethods.push(CUSTOM_METHOD); this.authMethods.push(CUSTOM_METHOD);
}
} else {
this.authMethods = this.authMethods.filter(element => element !== CUSTOM_METHOD);
} }
} else {
this.authMethods = this.authMethods.filter(element => element != CUSTOM_METHOD);
}
}
if (allowed) {
this.appNameForm.enable();
this.oidcForm.enable();
this.apiForm.enable();
}
if (this.app.oidcConfig?.redirectUrisList) {
this.redirectUrisList = this.app.oidcConfig.redirectUrisList;
}
if (this.app.oidcConfig?.postLogoutRedirectUrisList) {
this.postLogoutRedirectUrisList = this.app.oidcConfig.postLogoutRedirectUrisList;
}
if (this.app.oidcConfig?.clockSkew) {
const inSecs = this.app.oidcConfig?.clockSkew.seconds + this.app.oidcConfig?.clockSkew.nanos / 100000;
this.oidcForm.controls['clockSkewSeconds'].setValue(inSecs);
}
if (this.app.oidcConfig) {
this.oidcForm.patchValue(this.app.oidcConfig);
}
this.oidcForm.valueChanges.subscribe((oidcConfig) => {
this.initialAuthMethod = this.authMethodFromPartialConfig({ oidc: oidcConfig });
if (this.initialAuthMethod === CUSTOM_METHOD.key) {
if (!this.authMethods.includes(CUSTOM_METHOD)) {
this.authMethods.push(CUSTOM_METHOD);
}
} else {
this.authMethods = this.authMethods.filter(element => element != CUSTOM_METHOD);
} }
this.showSaveSnack(); if (allowed) {
}); this.appNameForm.enable();
this.oidcForm.enable();
this.apiForm.valueChanges.subscribe((apiConfig) => { this.apiForm.enable();
this.initialAuthMethod = this.authMethodFromPartialConfig({ api: apiConfig });
if (this.initialAuthMethod === CUSTOM_METHOD.key) {
if (!this.authMethods.includes(CUSTOM_METHOD)) {
this.authMethods.push(CUSTOM_METHOD);
}
} else {
this.authMethods = this.authMethods.filter(element => element != CUSTOM_METHOD);
} }
this.showSaveSnack(); if (this.app.oidcConfig?.redirectUrisList) {
}); this.redirectUrisList = this.app.oidcConfig.redirectUrisList;
} }
}).catch(error => { if (this.app.oidcConfig?.postLogoutRedirectUrisList) {
console.error(error); this.postLogoutRedirectUrisList = this.app.oidcConfig.postLogoutRedirectUrisList;
this.toast.showError(error); }
this.errorMessage = error.message; if (this.app.oidcConfig?.clockSkew) {
const inSecs = this.app.oidcConfig?.clockSkew.seconds +
this.app.oidcConfig?.clockSkew.nanos / 100000;
this.oidcForm.controls['clockSkewSeconds'].setValue(inSecs);
}
if (this.app.oidcConfig) {
this.oidcForm.patchValue(this.app.oidcConfig);
}
this.oidcForm.valueChanges.subscribe((oidcConfig) => {
this.initialAuthMethod = this.authMethodFromPartialConfig({ oidc: oidcConfig });
if (this.initialAuthMethod === CUSTOM_METHOD.key) {
if (!this.authMethods.includes(CUSTOM_METHOD)) {
this.authMethods.push(CUSTOM_METHOD);
}
} else {
this.authMethods = this.authMethods.filter(element => element !== CUSTOM_METHOD);
}
this.showSaveSnack();
});
this.apiForm.valueChanges.subscribe((apiConfig) => {
this.initialAuthMethod = this.authMethodFromPartialConfig({ api: apiConfig });
if (this.initialAuthMethod === CUSTOM_METHOD.key) {
if (!this.authMethods.includes(CUSTOM_METHOD)) {
this.authMethods.push(CUSTOM_METHOD);
}
} else {
this.authMethods = this.authMethods.filter(element => element !== CUSTOM_METHOD);
}
this.showSaveSnack();
});
}
}).catch(error => {
console.error(error);
this.toast.showError(error);
this.errorMessage = error.message;
});
}); });
});
this.docs = (await this.mgmtService.getOIDCInformation()); this.docs = (await this.mgmtService.getOIDCInformation());
} }
@ -308,7 +311,7 @@ export class AppDetailComponent implements OnInit, OnDestroy {
} }
private getAuthMethodOptions(type: string): void { private getAuthMethodOptions(type: string): void {
if (type == 'OIDC') { if (type === 'OIDC') {
switch (this.app.oidcConfig?.appType) { switch (this.app.oidcConfig?.appType) {
case OIDCAppType.OIDC_APP_TYPE_NATIVE: case OIDCAppType.OIDC_APP_TYPE_NATIVE:
this.authMethods = [ this.authMethods = [
@ -332,7 +335,7 @@ export class AppDetailComponent implements OnInit, OnDestroy {
break; break;
} }
} }
if (type == 'API') { if (type === 'API') {
this.authMethods = [ this.authMethods = [
PK_JWT_METHOD, PK_JWT_METHOD,
BASIC_AUTH_METHOD, BASIC_AUTH_METHOD,
@ -348,12 +351,24 @@ export class AppDetailComponent implements OnInit, OnDestroy {
public setPartialConfigFromAuthMethod(authMethod: string): void { public setPartialConfigFromAuthMethod(authMethod: string): void {
const partialConfig = getPartialConfigFromAuthMethod(authMethod); const partialConfig = getPartialConfigFromAuthMethod(authMethod);
if (partialConfig && partialConfig.oidc && this.app.oidcConfig) { if (partialConfig && partialConfig.oidc && this.app.oidcConfig) {
this.app.oidcConfig.responseTypesList = (partialConfig.oidc as Partial<OIDCConfig.AsObject>).responseTypesList ?? []; this.app.oidcConfig.responseTypesList =
this.app.oidcConfig.grantTypesList = (partialConfig.oidc as Partial<OIDCConfig.AsObject>).grantTypesList ?? []; (partialConfig.oidc as Partial<OIDCConfig.AsObject>).responseTypesList
this.app.oidcConfig.authMethodType = (partialConfig.oidc as Partial<OIDCConfig.AsObject>).authMethodType ?? OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE; ?? [];
this.app.oidcConfig.grantTypesList =
(partialConfig.oidc as Partial<OIDCConfig.AsObject>).grantTypesList
?? [];
this.app.oidcConfig.authMethodType =
(partialConfig.oidc as Partial<OIDCConfig.AsObject>).authMethodType
?? OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE;
this.oidcForm.patchValue(this.app.oidcConfig); this.oidcForm.patchValue(this.app.oidcConfig);
} else if (partialConfig && partialConfig.api && this.app.apiConfig) { } else if (partialConfig && partialConfig.api && this.app.apiConfig) {
this.app.apiConfig.authMethodType = (partialConfig.api as Partial<APIConfig.AsObject>).authMethodType ?? APIAuthMethodType.API_AUTH_METHOD_TYPE_BASIC; this.app.apiConfig.authMethodType =
(partialConfig.api as Partial<APIConfig.AsObject>).authMethodType
?? APIAuthMethodType.API_AUTH_METHOD_TYPE_BASIC;
this.apiAuthMethodType?.setValue(this.app.apiConfig.authMethodType); this.apiAuthMethodType?.setValue(this.app.apiConfig.authMethodType);
} }
} }

View File

@ -116,7 +116,7 @@ export function getPartialConfigFromAuthMethod(authMethod: string): {
responseTypesList: [OIDCResponseType.OIDC_RESPONSE_TYPE_CODE], responseTypesList: [OIDCResponseType.OIDC_RESPONSE_TYPE_CODE],
grantTypesList: [OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE], grantTypesList: [OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE],
authMethodType: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE, authMethodType: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE,
} },
}; };
return config; return config;
case POST_METHOD.key: case POST_METHOD.key:
@ -125,7 +125,7 @@ export function getPartialConfigFromAuthMethod(authMethod: string): {
responseTypesList: [OIDCResponseType.OIDC_RESPONSE_TYPE_CODE], responseTypesList: [OIDCResponseType.OIDC_RESPONSE_TYPE_CODE],
grantTypesList: [OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE], grantTypesList: [OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE],
authMethodType: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_POST, authMethodType: OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_POST,
} },
}; };
return config; return config;
case PK_JWT_METHOD.key: case PK_JWT_METHOD.key:
@ -137,7 +137,7 @@ export function getPartialConfigFromAuthMethod(authMethod: string): {
}, },
api: { api: {
authMethodType: APIAuthMethodType.API_AUTH_METHOD_TYPE_PRIVATE_KEY_JWT, authMethodType: APIAuthMethodType.API_AUTH_METHOD_TYPE_PRIVATE_KEY_JWT,
} },
}; };
return config; return config;
case BASIC_AUTH_METHOD.key: case BASIC_AUTH_METHOD.key:
@ -147,7 +147,7 @@ export function getPartialConfigFromAuthMethod(authMethod: string): {
}, },
api: { api: {
authMethodType: APIAuthMethodType.API_AUTH_METHOD_TYPE_BASIC, authMethodType: APIAuthMethodType.API_AUTH_METHOD_TYPE_BASIC,
} },
}; };
return config; return config;
case IMPLICIT_METHOD.key: case IMPLICIT_METHOD.key:
@ -159,7 +159,7 @@ export function getPartialConfigFromAuthMethod(authMethod: string): {
}, },
api: { api: {
authMethodType: APIAuthMethodType.API_AUTH_METHOD_TYPE_PRIVATE_KEY_JWT, authMethodType: APIAuthMethodType.API_AUTH_METHOD_TYPE_PRIVATE_KEY_JWT,
} },
}; };
return config; return config;
default: default:
@ -167,7 +167,12 @@ export function getPartialConfigFromAuthMethod(authMethod: string): {
} }
} }
export function getAuthMethodFromPartialConfig(config: { oidc?: Partial<OIDCConfig.AsObject>, api?: Partial<APIConfig.AsObject>; }): string { export function getAuthMethodFromPartialConfig(
config: {
oidc?: Partial<OIDCConfig.AsObject>,
api?: Partial<APIConfig.AsObject>,
},
): string {
if (config?.oidc) { if (config?.oidc) {
const toCheck = [config.oidc.responseTypesList, config.oidc.grantTypesList, config.oidc.authMethodType]; const toCheck = [config.oidc.responseTypesList, config.oidc.grantTypesList, config.oidc.authMethodType];
const code = JSON.stringify( const code = JSON.stringify(
@ -175,7 +180,7 @@ export function getAuthMethodFromPartialConfig(config: { oidc?: Partial<OIDCConf
[OIDCResponseType.OIDC_RESPONSE_TYPE_CODE], [OIDCResponseType.OIDC_RESPONSE_TYPE_CODE],
[OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE], [OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE],
OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_BASIC, OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_BASIC,
] ],
); );
const pkce = JSON.stringify( const pkce = JSON.stringify(
@ -183,7 +188,7 @@ export function getAuthMethodFromPartialConfig(config: { oidc?: Partial<OIDCConf
[OIDCResponseType.OIDC_RESPONSE_TYPE_CODE], [OIDCResponseType.OIDC_RESPONSE_TYPE_CODE],
[OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE], [OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE],
OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE, OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE,
] ],
); );
const post = JSON.stringify( const post = JSON.stringify(
@ -191,15 +196,15 @@ export function getAuthMethodFromPartialConfig(config: { oidc?: Partial<OIDCConf
[OIDCResponseType.OIDC_RESPONSE_TYPE_CODE], [OIDCResponseType.OIDC_RESPONSE_TYPE_CODE],
[OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE], [OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE],
OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_POST, OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_POST,
] ],
); );
const pk_jwt = JSON.stringify( const pkjwt = JSON.stringify(
[ [
[OIDCResponseType.OIDC_RESPONSE_TYPE_CODE], [OIDCResponseType.OIDC_RESPONSE_TYPE_CODE],
[OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE], [OIDCGrantType.OIDC_GRANT_TYPE_AUTHORIZATION_CODE],
OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_PRIVATE_KEY_JWT, OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_PRIVATE_KEY_JWT,
] ],
); );
const implicit = JSON.stringify( const implicit = JSON.stringify(
@ -207,14 +212,14 @@ export function getAuthMethodFromPartialConfig(config: { oidc?: Partial<OIDCConf
[OIDCResponseType.OIDC_RESPONSE_TYPE_ID_TOKEN_TOKEN], [OIDCResponseType.OIDC_RESPONSE_TYPE_ID_TOKEN_TOKEN],
[OIDCGrantType.OIDC_GRANT_TYPE_IMPLICIT], [OIDCGrantType.OIDC_GRANT_TYPE_IMPLICIT],
OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE, OIDCAuthMethodType.OIDC_AUTH_METHOD_TYPE_NONE,
] ],
); );
switch (JSON.stringify(toCheck)) { switch (JSON.stringify(toCheck)) {
case code: return CODE_METHOD.key; case code: return CODE_METHOD.key;
case pkce: return PKCE_METHOD.key; case pkce: return PKCE_METHOD.key;
case post: return POST_METHOD.key; case post: return POST_METHOD.key;
case pk_jwt: return PK_JWT_METHOD.key; case pkjwt: return PK_JWT_METHOD.key;
case implicit: return IMPLICIT_METHOD.key; case implicit: return IMPLICIT_METHOD.key;
default: default:
return CUSTOM_METHOD.key; return CUSTOM_METHOD.key;
@ -229,4 +234,4 @@ export function getAuthMethodFromPartialConfig(config: { oidc?: Partial<OIDCConf
} else { } else {
return CUSTOM_METHOD.key; return CUSTOM_METHOD.key;
} }
} }

View File

@ -8,8 +8,8 @@ import { OIDCAppType } from 'src/app/proto/generated/zitadel/app_pb';
// } // }
export enum AppCreateType { export enum AppCreateType {
API = "API", API = 'API',
OIDC = "OIDC" OIDC = 'OIDC',
} }
export interface RadioItemAppType { export interface RadioItemAppType {

View File

@ -15,10 +15,10 @@
<span class="uri" <span class="uri"
[ngClass]="{'green': !devMode && uri?.startsWith('https://'), 'red': !devMode && !uri?.startsWith('https://')}">{{uri}}</span> [ngClass]="{'green': !devMode && uri?.startsWith('https://'), 'red': !devMode && !uri?.startsWith('https://')}">{{uri}}</span>
<span class="fill-space"></span> <span class="fill-space"></span>
<i *ngIf="!devMode && !(uri | redirect : isNative)" class="las la-exclamation red"></i> <i *ngIf="!devMode && !(uri | redirect : isNative)" class="las la-exclamation red" [matTooltip]="isNative ? ('APP.OIDC.REDIRECTDESCRIPTIONNATIVE' | translate) : ('APP.OIDC.REDIRECTDESCRIPTIONWEB' | translate)"></i>
<button matTooltip="{{'ACTIONS.DELETE' | translate}}" mat-icon-button (click)="remove(uri)" class="icon-button"> <button matTooltip="{{'ACTIONS.DELETE' | translate}}" mat-icon-button (click)="remove(uri)" class="icon-button">
<mat-icon>cancel</mat-icon> <mat-icon class="icon">cancel</mat-icon>
</button> </button>
</div> </div>
</div> </div>

View File

@ -1,61 +1,62 @@
.form { .form {
display: flex; display: flex;
align-items: flex-end; align-items: flex-end;
min-width: 320px; min-width: 320px;
.formfield { .formfield {
flex: 1; flex: 1;
} }
button { button {
margin-bottom: 14px; margin-bottom: 14px;
margin-right: -0.5rem; margin-right: -.5rem;
} }
} }
.uri-list { .uri-list {
margin: 0 .5rem; margin: 0 .5rem;
width: 100%; width: 100%;
.uri-line { .uri-line {
display: flex; display: flex;
align-items: center; align-items: center;
.uri { .uri {
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
font-size: 14px; font-size: 14px;
}
.fill-space {
flex: 1;
}
i.green {
font-size: 1rem;
line-height: 35px;
height: 30px;
}
i.red {
font-size: 1.2rem;
}
.icon-button {
height: 30px;
line-height: 30px;
mat-icon {
font-size: 1rem !important;
}
&:not(:hover) {
color: var(--grey);
}
}
} }
.fill-space {
flex: 1;
}
i.green {
font-size: 1rem;
line-height: 35px;
height: 30px;
}
i.red {
font-size: 1.2rem;
}
.icon-button {
height: 30px;
line-height: 30px;
.icon {
font-size: 1rem !important;
}
&:not(:hover) {
color: var(--grey);
}
}
}
} }
.error { .error {
font-size: 13px; font-size: 13px;
color: #f44336; color: #f44336;
margin: 0 .5rem 1.5rem .5rem; margin: 0 .5rem 1.5rem .5rem;
} }

View File

@ -8,7 +8,7 @@ describe('RedirectUrisComponent', () => {
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ RedirectUrisComponent ] declarations: [ RedirectUrisComponent ],
}) })
.compileComponents(); .compileComponents();
}); });

View File

@ -5,7 +5,7 @@ import { Observable, Subscription } from 'rxjs';
@Component({ @Component({
selector: 'cnsl-redirect-uris', selector: 'cnsl-redirect-uris',
templateUrl: './redirect-uris.component.html', templateUrl: './redirect-uris.component.html',
styleUrls: ['./redirect-uris.component.scss'] styleUrls: ['./redirect-uris.component.scss'],
}) })
export class RedirectUrisComponent implements OnInit, OnDestroy { export class RedirectUrisComponent implements OnInit, OnDestroy {
@Input() title: string = ''; @Input() title: string = '';

View File

@ -74,8 +74,8 @@
<i class="las la-exclamation"></i> <i class="las la-exclamation"></i>
<span>{{'PROJECT.TABLE.EMPTY' | translate}}</span> <span>{{'PROJECT.TABLE.EMPTY' | translate}}</span>
</div> </div>
<mat-paginator class="paginator" #paginator [length]="totalResult" [pageSize]="10" <cnsl-paginator class="paginator" #paginator [timestamp]="viewTimestamp" [length]="totalResult" [pageSize]="10"
[pageSizeOptions]="[5, 10, 20]" (page)="changePage($event)"></mat-paginator> [pageSizeOptions]="[5, 10, 20]" (page)="changePage($event)"></cnsl-paginator>
</div> </div>
</app-refresh-table> </app-refresh-table>
</div> </div>

View File

@ -1,12 +1,12 @@
import { animate, animateChild, query, stagger, style, transition, trigger } from '@angular/animations'; import { animate, animateChild, query, stagger, style, transition, trigger } from '@angular/animations';
import { SelectionModel } from '@angular/cdk/collections'; import { SelectionModel } from '@angular/cdk/collections';
import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { MatPaginator, PageEvent } from '@angular/material/paginator';
import { MatTableDataSource } from '@angular/material/table'; import { MatTableDataSource } from '@angular/material/table';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb'; import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb';
import { BehaviorSubject, Observable, Subscription } from 'rxjs'; import { BehaviorSubject, Observable, Subscription } from 'rxjs';
import { PageEvent, PaginatorComponent } from 'src/app/modules/paginator/paginator.component';
import { GrantedProject } from 'src/app/proto/generated/zitadel/project_pb'; import { GrantedProject } from 'src/app/proto/generated/zitadel/project_pb';
import { ManagementService } from 'src/app/services/mgmt.service'; import { ManagementService } from 'src/app/services/mgmt.service';
import { ToastService } from 'src/app/services/toast.service'; import { ToastService } from 'src/app/services/toast.service';
@ -41,7 +41,7 @@ export class GrantedProjectListComponent implements OnInit, OnDestroy {
public dataSource: MatTableDataSource<GrantedProject.AsObject> = public dataSource: MatTableDataSource<GrantedProject.AsObject> =
new MatTableDataSource<GrantedProject.AsObject>(); new MatTableDataSource<GrantedProject.AsObject>();
@ViewChild(MatPaginator) public paginator!: MatPaginator; @ViewChild(PaginatorComponent) public paginator!: PaginatorComponent;
public grantedProjectList: GrantedProject.AsObject[] = []; public grantedProjectList: GrantedProject.AsObject[] = [];
public displayedColumns: string[] = ['select', 'name', 'resourceOwnerName', 'state', 'creationDate', 'changeDate']; public displayedColumns: string[] = ['select', 'name', 'resourceOwnerName', 'state', 'creationDate', 'changeDate'];

View File

@ -1,4 +1,4 @@
<div class="max-width-container"> <div class="enlarged-container">
<h1>{{ 'PROJECT.PAGES.LIST' | translate }}</h1> <h1>{{ 'PROJECT.PAGES.LIST' | translate }}</h1>
<p class="sub">{{ 'PROJECT.PAGES.LISTDESCRIPTION' | translate }}</p> <p class="sub">{{ 'PROJECT.PAGES.LISTDESCRIPTION' | translate }}</p>

View File

@ -4,7 +4,6 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { MatCheckboxModule } from '@angular/material/checkbox'; import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
import { MatPaginatorModule } from '@angular/material/paginator';
import { MatProgressBarModule } from '@angular/material/progress-bar'; import { MatProgressBarModule } from '@angular/material/progress-bar';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatSelectModule } from '@angular/material/select'; import { MatSelectModule } from '@angular/material/select';
@ -20,6 +19,7 @@ import { ChangesModule } from 'src/app/modules/changes/changes.module';
import { ContributorsModule } from 'src/app/modules/contributors/contributors.module'; import { ContributorsModule } from 'src/app/modules/contributors/contributors.module';
import { InputModule } from 'src/app/modules/input/input.module'; import { InputModule } from 'src/app/modules/input/input.module';
import { MetaLayoutModule } from 'src/app/modules/meta-layout/meta-layout.module'; import { MetaLayoutModule } from 'src/app/modules/meta-layout/meta-layout.module';
import { PaginatorModule } from 'src/app/modules/paginator/paginator.module';
import { ProjectRolesModule } from 'src/app/modules/project-roles/project-roles.module'; import { ProjectRolesModule } from 'src/app/modules/project-roles/project-roles.module';
import { RefreshTableModule } from 'src/app/modules/refresh-table/refresh-table.module'; import { RefreshTableModule } from 'src/app/modules/refresh-table/refresh-table.module';
import { SharedModule } from 'src/app/modules/shared/shared.module'; import { SharedModule } from 'src/app/modules/shared/shared.module';
@ -51,7 +51,7 @@ import { GrantedProjectsComponent } from './granted-projects.component';
ReactiveFormsModule, ReactiveFormsModule,
HasRoleModule, HasRoleModule,
MatTableModule, MatTableModule,
MatPaginatorModule, PaginatorModule,
InputModule, InputModule,
ChangesModule, ChangesModule,
MatIconModule, MatIconModule,

Some files were not shown because too many files have changed in this diff Show More