From dd9dcf9f7297f051f60802d8cd556fed416ee0c4 Mon Sep 17 00:00:00 2001 From: Ramon Date: Wed, 22 Apr 2026 11:46:27 +0200 Subject: [PATCH] feat(console): improve org search performance in create project grant page #11121 (#12057) # Which Problems Are Solved When creating a new Project Grant in the Zitadel console, the UI becomes extremely sluggish/unusable. The issue appears tied to the organization search dropdown attempting to load a very large dataset. # How the Problems Are Solved The refactored `SearchOrgAutocompleteComponent` uses server side filtering and ondemand loading using infinite scrolling. This makes sure on a subset of organizations is ever loaded on the client. # Additional Changes - Added tailwindcss for easier styling - Removed build warnings - Removed some of the SCSS syntax warnings # Additional Context - Closes #11121 --------- Co-authored-by: Max Peintner --- apps/login/package.json | 2 +- console/.postcssrc.json | 5 + console/angular.json | 47 ++--- console/package.json | 5 +- .../framework-autocomplete.component.scss | 8 +- .../framework-change.component.scss | 10 +- .../oidc-configuration.component.html | 8 +- .../quickstart/quickstart.component.scss | 8 +- .../scrollable/scrollable.directive.ts | 4 +- .../accounts-card.component.scss | 2 +- .../action-keys/action-keys.component.scss | 2 +- .../app-auth-method-radio.component.scss | 8 +- .../app-type-radio.component.scss | 8 +- console/src/app/modules/card/card.scss | 6 +- .../domain-verification.component.scss | 2 +- .../edit-text/edit-text.component.scss | 2 +- .../app/modules/events/events.component.scss | 2 +- .../filter-events.component.scss | 8 +- .../app/modules/filter/filter.component.scss | 6 +- .../app/modules/footer/footer.component.scss | 4 +- .../app/modules/header/header.component.scss | 10 +- .../idp-table/idp-table.component.html | 2 +- .../idp-table/idp-table.component.scss | 4 +- .../info-overlay/info-overlay.component.scss | 4 +- .../modules/info-row/info-row.component.html | 122 ++++++------ .../keyboard-shortcuts.component.scss | 4 +- .../app/modules/label/label.component.scss | 2 +- .../nav-toggle/nav-toggle.component.scss | 6 +- .../src/app/modules/nav/nav.component.scss | 14 +- .../onboarding-card.component.scss | 2 +- .../onboarding/onboarding.component.scss | 6 +- .../idp-settings/idp-settings.component.scss | 4 +- .../notification-smtp-provider.component.scss | 4 +- .../preview/preview.component.scss | 2 +- .../private-labeling-policy.component.scss | 4 +- .../project-role-chip.component.scss | 4 +- .../project-roles-table.component.html | 2 +- .../src/app/modules/providers/providers.scss | 4 +- .../search-org-autocomplete.component.html | 81 +++++--- .../search-org-autocomplete.component.scss | 38 ---- .../search-org-autocomplete.component.spec.ts | 24 --- .../search-org-autocomplete.component.ts | 184 +++++++++++------- .../search-org-autocomplete.module.ts | 32 --- .../search-user-autocomplete.component.scss | 4 +- .../shortcuts/shortcuts.component.scss | 4 +- .../modules/sidenav/sidenav.component.scss | 4 +- .../smtp-table/smtp-table.component.html | 3 +- .../smtp-table/smtp-table.component.scss | 4 +- .../string-list/string-list.component.scss | 2 +- .../table-actions.component.scss | 2 +- .../theme-setting.component.scss | 4 +- .../user-grants/user-grants.component.html | 2 +- .../src/app/pages/home/home.component.scss | 4 +- .../pages/org-actions/actions.component.html | 10 +- .../pages/org-actions/actions.component.ts | 30 +-- .../apps/app-detail/app-detail.component.html | 8 +- .../apps/app-detail/app-detail.component.scss | 2 +- .../apps/integrate/integrate.component.scss | 8 +- .../project-grant-create.component.html | 2 +- .../project-grant-create.component.ts | 49 ++--- .../project-grant-create.module.ts | 4 +- .../project-grant-detail.component.html | 24 +-- .../project-grant-detail.component.ts | 93 +++------ .../project-grant-illustration.component.scss | 6 +- .../project-grants.component.html | 2 +- .../project-list/project-list.component.html | 2 +- .../auth-passwordless.component.html | 2 +- .../auth-user-mfa.component.html | 2 +- .../external-idps.component.html | 2 +- .../passwordless/passwordless.component.html | 2 +- .../user-table/user-table.component.html | 2 +- console/src/app/services/new-auth.service.ts | 7 +- console/src/app/services/new-mgmt.service.ts | 5 + console/src/styles/codemirror.scss | 4 +- console/src/styles/input.scss | 12 +- console/src/styles/table.scss | 28 +-- console/src/tailwind.css | 5 + pnpm-lock.yaml | 47 +++-- tests/functional-ui/package.json | 2 +- 79 files changed, 517 insertions(+), 582 deletions(-) create mode 100644 console/.postcssrc.json delete mode 100644 console/src/app/modules/search-org-autocomplete/search-org-autocomplete.component.scss delete mode 100644 console/src/app/modules/search-org-autocomplete/search-org-autocomplete.component.spec.ts delete mode 100644 console/src/app/modules/search-org-autocomplete/search-org-autocomplete.module.ts create mode 100644 console/src/tailwind.css diff --git a/apps/login/package.json b/apps/login/package.json index 4bdfcc83f..4faa9c9d8 100644 --- a/apps/login/package.json +++ b/apps/login/package.json @@ -101,7 +101,7 @@ "node-forge": "^1.4.0", "nodemon": "^3.1.14", "postcss": "^8.5.8", - "prettier": "^3.8.1", + "prettier": "^3.8.3", "prettier-plugin-organize-imports": "^4.3.0", "prettier-plugin-tailwindcss": "^0.7.2", "sass": "^1.98.0", diff --git a/console/.postcssrc.json b/console/.postcssrc.json new file mode 100644 index 000000000..e092dc7c1 --- /dev/null +++ b/console/.postcssrc.json @@ -0,0 +1,5 @@ +{ + "plugins": { + "@tailwindcss/postcss": {} + } +} diff --git a/console/angular.json b/console/angular.json index 826073948..3d641e0b7 100644 --- a/console/angular.json +++ b/console/angular.json @@ -22,9 +22,7 @@ "base": "dist/console" }, "index": "src/index.html", - "polyfills": [ - "zone.js" - ], + "polyfills": ["zone.js"], "tsConfig": "tsconfig.app.json", "inlineStyleLanguage": "scss", "assets": [ @@ -37,23 +35,13 @@ "output": "assets/docs/img/tech" } ], - "styles": [ - "src/styles.scss" - ], - "scripts": [ - "./node_modules/tinycolor2/dist/tinycolor-min.js" - ], + "styles": ["src/styles.scss", "src/tailwind.css"], + "scripts": ["./node_modules/tinycolor2/dist/tinycolor-min.js"], "stylePreprocessorOptions": { "sass": { - "silenceDeprecations": [ - "import", - "mixed-decls" - ] + "silenceDeprecations": ["import", "mixed-decls"] }, - "includePaths": [ - "node_modules", - "." - ] + "includePaths": ["node_modules", "."] }, "allowedCommonJsDependencies": [ "opentype.js", @@ -158,29 +146,18 @@ "test": { "builder": "@angular/build:karma", "options": { - "polyfills": [ - "zone.js", - "zone.js/testing" - ], + "polyfills": ["zone.js", "zone.js/testing"], "tsConfig": "tsconfig.spec.json", "inlineStyleLanguage": "scss", - "assets": [ - "src/favicon.ico", - "src/assets" - ], - "styles": [ - "src/styles.scss" - ], + "assets": ["src/favicon.ico", "src/assets"], + "styles": ["src/styles.scss"], "scripts": [] } }, "lint": { "builder": "@angular-eslint/builder:lint", "options": { - "lintFilePatterns": [ - "src/**/*.ts", - "src/**/*.html" - ] + "lintFilePatterns": ["src/**/*.ts", "src/**/*.html"] } } } @@ -188,9 +165,7 @@ }, "cli": { "analytics": false, - "schematicCollections": [ - "@angular-eslint/schematics" - ], + "schematicCollections": ["@angular-eslint/schematics"], "packageManager": "pnpm" }, "schematics": { @@ -219,4 +194,4 @@ "typeSeparator": "." } } -} \ No newline at end of file +} diff --git a/console/package.json b/console/package.json index 93e7dd711..a9c69e6c3 100644 --- a/console/package.json +++ b/console/package.json @@ -75,6 +75,7 @@ "@types/opentype.js": "^1.3.9", "@types/qrcode": "^1.5.6", "@types/uuid": "^11.0.0", + "@tailwindcss/postcss": "^4.2.2", "@typescript-eslint/eslint-plugin": "^8.58.0", "@typescript-eslint/parser": "^8.58.0", "@zitadel/client": "workspace:*", @@ -92,8 +93,10 @@ "karma-coverage-istanbul-reporter": "^3.0.3", "karma-jasmine": "^5.1.0", "karma-jasmine-html-reporter": "^2.2.0", - "prettier": "^3.8.1", + "postcss": "^8.5.8", + "prettier": "^3.8.3", "prettier-plugin-organize-imports": "^4.3.0", + "tailwindcss": "^4.2.2", "typescript": "5.9.2" } } diff --git a/console/src/app/components/framework-autocomplete/framework-autocomplete.component.scss b/console/src/app/components/framework-autocomplete/framework-autocomplete.component.scss index ed0eaf2c2..f57fc1d41 100644 --- a/console/src/app/components/framework-autocomplete/framework-autocomplete.component.scss +++ b/console/src/app/components/framework-autocomplete/framework-autocomplete.component.scss @@ -13,8 +13,8 @@ $list-background-color: map-get($background, 300); $card-background-color: map-get($background, cards); - $border-color: if($is-dark-theme, rgba(#8795a1, 0.2), rgba(#8795a1, 0.2)); - $border-selected-color: if($is-dark-theme, #fff, #000); + $border-color: if(sass($is-dark-theme): rgba(#8795a1, 0.2) ; else: rgba(#8795a1, 0.2)); + $border-selected-color: if(sass($is-dark-theme): #fff; else: #000); .full-width { width: 100%; @@ -58,11 +58,11 @@ } .dark-only { - display: if($is-dark-theme, block, none); + display: if(sass($is-dark-theme): block; else: none); } .light-only { - display: if($is-dark-theme, none, block); + display: if(sass($is-dark-theme): none; else: block); } } } diff --git a/console/src/app/components/framework-change/framework-change.component.scss b/console/src/app/components/framework-change/framework-change.component.scss index e89ba17ac..0a34d0bfd 100644 --- a/console/src/app/components/framework-change/framework-change.component.scss +++ b/console/src/app/components/framework-change/framework-change.component.scss @@ -13,8 +13,8 @@ $list-background-color: map-get($background, 300); $card-background-color: map-get($background, cards); - $border-color: if($is-dark-theme, rgba(#8795a1, 0.2), rgba(#8795a1, 0.2)); - $border-selected-color: if($is-dark-theme, #fff, #000); + $border-color: if(sass($is-dark-theme): rgba(#8795a1, 0.2) ; else: rgba(#8795a1, 0.2)); + $border-selected-color: if(sass($is-dark-theme): #fff; else: #000); .framework-change-wrapper { .framework-card-wrapper { @@ -36,7 +36,7 @@ align-items: center; justify-content: center; width: fit-content; - // background-color: if($is-dark-theme, map-get($background, state), #e4e7e4); + // background-color: if(sass($is-dark-theme): map-get($background, state); else: #e4e7e4); // box-shadow: 0 0 3px #0000001a; border: 1px solid rgba(#8795a1, 0.2); padding: 0 0.5rem; @@ -51,11 +51,11 @@ } .dark-only { - display: if($is-dark-theme, block, none); + display: if(sass($is-dark-theme): block; else: none); } .light-only { - display: if($is-dark-theme, none, block); + display: if(sass($is-dark-theme): none; else: block); } span { diff --git a/console/src/app/components/oidc-configuration/oidc-configuration.component.html b/console/src/app/components/oidc-configuration/oidc-configuration.component.html index 1dab607c6..a04b617cc 100644 --- a/console/src/app/components/oidc-configuration/oidc-configuration.component.html +++ b/console/src/app/components/oidc-configuration/oidc-configuration.component.html @@ -21,10 +21,10 @@ {{ 'APP.GRANT' | translate }} - @if (configuration.grantTypesList && configuration.grantTypesList.length > 0) { + @if (configuration.grantTypesList && configuration.grantTypesList.length) { [ - @for (element of configuration.grantTypesList ?? []; track element; let i = $index) { + @for (element of configuration.grantTypesList; track element; let i = $index) { {{ 'APP.OIDC.GRANT.' + element | translate }} {{ i < configuration.grantTypesList.length - 1 ? ', ' : '' }} @@ -38,10 +38,10 @@ {{ 'APP.OIDC.RESPONSETYPE' | translate }} - @if (configuration.responseTypesList && configuration.responseTypesList.length > 0) { + @if (configuration.responseTypesList && configuration.responseTypesList.length) { [ - @for (element of configuration.responseTypesList ?? []; track element; let i = $index) { + @for (element of configuration.responseTypesList; track element; let i = $index) { {{ 'APP.OIDC.RESPONSE.' + element | translate }} {{ i < configuration.responseTypesList.length - 1 ? ', ' : '' }} diff --git a/console/src/app/components/quickstart/quickstart.component.scss b/console/src/app/components/quickstart/quickstart.component.scss index 5b1ecb571..f3ca36c7a 100644 --- a/console/src/app/components/quickstart/quickstart.component.scss +++ b/console/src/app/components/quickstart/quickstart.component.scss @@ -13,8 +13,8 @@ $list-background-color: map-get($background, 300); $card-background-color: map-get($background, cards); - $border-color: if($is-dark-theme, rgba(#8795a1, 0.2), rgba(#8795a1, 0.2)); - $border-selected-color: if($is-dark-theme, #fff, #000); + $border-color: if(sass($is-dark-theme): rgba(#8795a1, 0.2) ; else: rgba(#8795a1, 0.2)); + $border-selected-color: if(sass($is-dark-theme): #fff; else: #000); .quickstart-header { display: flex; @@ -86,11 +86,11 @@ } .dark-only { - display: if($is-dark-theme, block, none); + display: if(sass($is-dark-theme): block; else: none); } .light-only { - display: if($is-dark-theme, none, block); + display: if(sass($is-dark-theme): none; else: block); } span { diff --git a/console/src/app/directives/scrollable/scrollable.directive.ts b/console/src/app/directives/scrollable/scrollable.directive.ts index cd38ddf58..eff5c7dbe 100644 --- a/console/src/app/directives/scrollable/scrollable.directive.ts +++ b/console/src/app/directives/scrollable/scrollable.directive.ts @@ -6,9 +6,9 @@ import { Directive, ElementRef, EventEmitter, HostListener, Output } from '@angu }) export class ScrollableDirective { // when using this directive, add overflow-y scroll to css - @Output() scrollPosition: EventEmitter = new EventEmitter(); + @Output() scrollPosition: EventEmitter<'bottom' | 'top'> = new EventEmitter(); - constructor(public el: ElementRef) {} + constructor(private readonly el: ElementRef) {} @HostListener('scroll', ['$event']) public onScroll(event: any): void { diff --git a/console/src/app/modules/accounts-card/accounts-card.component.scss b/console/src/app/modules/accounts-card/accounts-card.component.scss index c4926ca89..d3db3082e 100644 --- a/console/src/app/modules/accounts-card/accounts-card.component.scss +++ b/console/src/app/modules/accounts-card/accounts-card.component.scss @@ -11,7 +11,7 @@ $foreground: map-get($theme, foreground); $is-dark-theme: map-get($theme, is-dark); $back: map-get($background, background); - $border-color: if($is-dark-theme, rgba(#8795a1, 0.2), rgba(#8795a1, 0.2)); + $border-color: if(sass($is-dark-theme): rgba(#8795a1, 0.2) ; else: rgba(#8795a1, 0.2)); $secondary-text: map-get($foreground, secondary-text); .accounts-card { diff --git a/console/src/app/modules/action-keys/action-keys.component.scss b/console/src/app/modules/action-keys/action-keys.component.scss index 102019f9c..c4d372aab 100644 --- a/console/src/app/modules/action-keys/action-keys.component.scss +++ b/console/src/app/modules/action-keys/action-keys.component.scss @@ -62,7 +62,7 @@ .action-keys-row { .key-overlay { z-index: 0; - background: if($is-dark-theme, #fff, #000); + background: if(sass($is-dark-theme): #fff; else: #000); opacity: 0.15; } } diff --git a/console/src/app/modules/app-radio/app-auth-method-radio/app-auth-method-radio.component.scss b/console/src/app/modules/app-radio/app-auth-method-radio/app-auth-method-radio.component.scss index d1d660688..5b6736a03 100644 --- a/console/src/app/modules/app-radio/app-auth-method-radio/app-auth-method-radio.component.scss +++ b/console/src/app/modules/app-radio/app-auth-method-radio/app-auth-method-radio.component.scss @@ -3,7 +3,7 @@ $primary-color: map-get($primary, 500); $is-dark-theme: map-get($theme, is-dark); $background: map-get($theme, background); - $border-color: if($is-dark-theme, rgba(#8795a1, 0.2), rgba(#8795a1, 0.2)); + $border-color: if(sass($is-dark-theme): rgba(#8795a1, 0.2) ; else: rgba(#8795a1, 0.2)); .auth-method-radio-button-wrapper { display: flex; @@ -29,7 +29,7 @@ box-sizing: border-box; &:focus { - border: 1px solid if($is-dark-theme, white, black); + border: 1px solid if(sass($is-dark-theme): white; else: black); } } @@ -39,7 +39,7 @@ box-shadow: 0 0 6px rgb(0 0 0 / 10%); .cnsl-radio-header span { - color: if($is-dark-theme, white, white); + color: if(sass($is-dark-theme): white; else: white); } } @@ -108,7 +108,7 @@ span { margin: 2rem; font-size: 30px; - color: if($is-dark-theme, #00000050, #ffffff50); + color: if(sass($is-dark-theme): #00000050; else: #ffffff50); } } diff --git a/console/src/app/modules/app-radio/app-type-radio/app-type-radio.component.scss b/console/src/app/modules/app-radio/app-type-radio/app-type-radio.component.scss index 10523c9f0..55d1ca3c7 100644 --- a/console/src/app/modules/app-radio/app-type-radio/app-type-radio.component.scss +++ b/console/src/app/modules/app-radio/app-type-radio/app-type-radio.component.scss @@ -11,7 +11,7 @@ $primary-color: map-get($primary, 500); $is-dark-theme: map-get($theme, is-dark); $background: map-get($theme, background); - $border-color: if($is-dark-theme, rgba(#8795a1, 0.2), rgba(#8795a1, 0.2)); + $border-color: if(sass($is-dark-theme): rgba(#8795a1, 0.2) ; else: rgba(#8795a1, 0.2)); input[type='radio'].app { appearance: none; @@ -20,7 +20,7 @@ box-sizing: border-box; &:focus { - border: 1px solid if($is-dark-theme, white, black); + border: 1px solid if(sass($is-dark-theme): white; else: black); } } @@ -31,7 +31,7 @@ box-shadow: 0 0 6px rgb(0 0 0 / 10%); .cnsl-type-radio-header span { - color: if($is-dark-theme, white, white); + color: if(sass($is-dark-theme): white; else: white); } } @@ -64,7 +64,7 @@ span { margin: 2rem; font-size: 30px; - color: if($is-dark-theme, #00000050, #ffffff50); + color: if(sass($is-dark-theme): #00000050; else: #ffffff50); } } diff --git a/console/src/app/modules/card/card.scss b/console/src/app/modules/card/card.scss index 89e7a324c..e3d6d1e3f 100644 --- a/console/src/app/modules/card/card.scss +++ b/console/src/app/modules/card/card.scss @@ -4,8 +4,8 @@ $background: map-get($theme, background); $card-background-color: map-get($background, cards); $is-dark-theme: map-get($theme, is-dark); - $border-color: if($is-dark-theme, rgba(#8795a1, 0.2), rgba(#8795a1, 0.2)); - $border-selected-color: if($is-dark-theme, #fff, #000); + $border-color: if(sass($is-dark-theme): rgba(#8795a1, 0.2) ; else: rgba(#8795a1, 0.2)); + $border-selected-color: if(sass($is-dark-theme): #fff; else: #000); .card { background-color: $card-background-color; @@ -57,7 +57,7 @@ .cnsl-chip { height: auto; border: 1px solid $border-color; - background: if($is-dark-theme, map-get($background, state), #e4e7e4) !important; + background: if(sass($is-dark-theme): map-get($background, state) ; else: #e4e7e4) !important; .cnsl-chip-content { display: flex; diff --git a/console/src/app/modules/domains/domain-verification/domain-verification.component.scss b/console/src/app/modules/domains/domain-verification/domain-verification.component.scss index 8ffce8a77..0598eca2a 100644 --- a/console/src/app/modules/domains/domain-verification/domain-verification.component.scss +++ b/console/src/app/modules/domains/domain-verification/domain-verification.component.scss @@ -1,6 +1,6 @@ @mixin domain-verification-theme($theme) { $is-dark-theme: map-get($theme, is-dark); - $border-color: if($is-dark-theme, rgba(#8795a1, 0.2), rgba(#8795a1, 0.2)); + $border-color: if(sass($is-dark-theme): rgba(#8795a1, 0.2) ; else: rgba(#8795a1, 0.2)); .entry { margin: 0.5rem 0; diff --git a/console/src/app/modules/edit-text/edit-text.component.scss b/console/src/app/modules/edit-text/edit-text.component.scss index c81a4700c..68f31ed48 100644 --- a/console/src/app/modules/edit-text/edit-text.component.scss +++ b/console/src/app/modules/edit-text/edit-text.component.scss @@ -24,7 +24,7 @@ min-height: 80px; &.defaulttext { - color: if($is-dark-theme, #ffffff50, #00000050); + color: if(sass($is-dark-theme): #ffffff50; else: #00000050); } } diff --git a/console/src/app/modules/events/events.component.scss b/console/src/app/modules/events/events.component.scss index 9c0887839..0aec1461d 100644 --- a/console/src/app/modules/events/events.component.scss +++ b/console/src/app/modules/events/events.component.scss @@ -18,7 +18,7 @@ $foreground: map-get($theme, foreground); $is-dark-theme: map-get($theme, is-dark); $card-background-color: map-get($background, cards); - $border-color: if($is-dark-theme, rgba(#8795a1, 0.2), rgba(#8795a1, 0.2)); + $border-color: if(sass($is-dark-theme): rgba(#8795a1, 0.2) ; else: rgba(#8795a1, 0.2)); $primary: map-get($theme, primary); $primary-color: map-get($primary, 500); diff --git a/console/src/app/modules/filter-events/filter-events.component.scss b/console/src/app/modules/filter-events/filter-events.component.scss index 8dec65a47..963e3832f 100644 --- a/console/src/app/modules/filter-events/filter-events.component.scss +++ b/console/src/app/modules/filter-events/filter-events.component.scss @@ -9,8 +9,8 @@ $secondary-text: map-get($foreground, secondary-text); $is-dark-theme: map-get($theme, is-dark); - $link-hover-color: if($is-dark-theme, map-get($primary, 200), $primary-color); - $link-color: if($is-dark-theme, $lighter-primary-color, $primary-color); + $link-hover-color: if(sass($is-dark-theme): map-get($primary, 200) ; else: $primary-color); + $link-color: if(sass($is-dark-theme): $lighter-primary-color; else: $primary-color); .filter-events-wrapper { border-radius: 0.5rem; @@ -38,7 +38,7 @@ justify-content: space-between; align-items: center; padding: 0 0.5rem 0.5rem 0.5rem; - border-bottom: 2px solid if($is-dark-theme, #ffffff15, #00000015); + border-bottom: 2px solid if(sass($is-dark-theme): #ffffff15; else: #00000015); .filter-middle { margin: 0 1rem; @@ -73,7 +73,7 @@ align-items: center; justify-content: space-around; padding: 0 0.5rem; - background-color: if($is-dark-theme, #00000020, #00000008); + background-color: if(sass($is-dark-theme): #00000020; else: #00000008); margin: 0 -0.5rem; .aggregate-type-select .mat-mdc-select { diff --git a/console/src/app/modules/filter/filter.component.scss b/console/src/app/modules/filter/filter.component.scss index 8cbfd29fc..c502172f5 100644 --- a/console/src/app/modules/filter/filter.component.scss +++ b/console/src/app/modules/filter/filter.component.scss @@ -11,7 +11,7 @@ $foreground: map-get($theme, foreground); $is-dark-theme: map-get($theme, is-dark); $back: map-get($background, background); - $border-color: if($is-dark-theme, rgba(#8795a1, 0.2), rgba(#8795a1, 0.2)); + $border-color: if(sass($is-dark-theme): rgba(#8795a1, 0.2) ; else: rgba(#8795a1, 0.2)); .cnsl-filter-button-wrapper { position: relative; @@ -48,7 +48,7 @@ justify-content: space-between; align-items: center; padding-bottom: 0.5rem; - border-bottom: 2px solid if($is-dark-theme, #ffffff15, #00000015); + border-bottom: 2px solid if(sass($is-dark-theme): #ffffff15; else: #00000015); .filter-middle { margin: 0 1rem; @@ -74,7 +74,7 @@ align-items: center; justify-content: space-between; padding: 0 0.5rem; - background-color: if($is-dark-theme, #00000020, #00000008); + background-color: if(sass($is-dark-theme): #00000020; else: #00000008); margin: 0 -0.5rem; .nomethod { diff --git a/console/src/app/modules/footer/footer.component.scss b/console/src/app/modules/footer/footer.component.scss index fffb5e927..66cb91a2b 100644 --- a/console/src/app/modules/footer/footer.component.scss +++ b/console/src/app/modules/footer/footer.component.scss @@ -13,7 +13,7 @@ $is-dark-theme: map-get($theme, is-dark); $back: map-get($background, background); $secondary-text: map-get($foreground, secondary-text); - $link-hover-color: if($is-dark-theme, $lighter-primary-color, $darker-primary-color); + $link-hover-color: if(sass($is-dark-theme): $lighter-primary-color; else: $darker-primary-color); .footer-wrapper { padding: 2rem; @@ -71,7 +71,7 @@ .swisssvg { width: 14rem; margin: 1rem 0; - color: if($is-dark-theme, #ffffff60, #00000060); + color: if(sass($is-dark-theme): #ffffff60; else: #00000060); .cross { fill: white; diff --git a/console/src/app/modules/header/header.component.scss b/console/src/app/modules/header/header.component.scss index 219e7d2e7..ac2809ca5 100644 --- a/console/src/app/modules/header/header.component.scss +++ b/console/src/app/modules/header/header.component.scss @@ -64,7 +64,7 @@ // z-index: 502; &:hover { - background-color: if($is-dark-theme, #ffffff10, #00000010); + background-color: if(sass($is-dark-theme): #ffffff10; else: #00000010); } @media only screen and (max-width: 700px) { @@ -97,7 +97,7 @@ transition: all ease 0.2s; &:hover { - background-color: if($is-dark-theme, #ffffff10, #00000010); + background-color: if(sass($is-dark-theme): #ffffff10; else: #00000010); } .svgspan { @@ -146,7 +146,7 @@ } &:hover { - background-color: if($is-dark-theme, #ffffff10, #00000010); + background-color: if(sass($is-dark-theme): #ffffff10; else: #00000010); } .mob { @@ -274,7 +274,7 @@ border-radius: 50%; transition: border ease 0.15s; padding: 0; - border: 2px solid if($is-dark-theme, #ffffff80, #00000010); + border: 2px solid if(sass($is-dark-theme): #ffffff80; else: #00000010); outline: none; background: none; @@ -284,7 +284,7 @@ &:hover, &:focus { - border: 2px solid if($is-dark-theme, #fff, map-get($primary, 200)); + border: 2px solid if(sass($is-dark-theme): #fff; else: map-get($primary, 200)); } .docs { diff --git a/console/src/app/modules/idp-table/idp-table.component.html b/console/src/app/modules/idp-table/idp-table.component.html index cc6a2ebef..0362dbba3 100644 --- a/console/src/app/modules/idp-table/idp-table.component.html +++ b/console/src/app/modules/idp-table/idp-table.component.html @@ -234,7 +234,7 @@ - @if ((loading$ | async) === false && !dataSource?.data?.length) { + @if ((loading$ | async) === false && !dataSource.data.length) {
{{ 'IDP.EMPTY' | translate }} diff --git a/console/src/app/modules/idp-table/idp-table.component.scss b/console/src/app/modules/idp-table/idp-table.component.scss index 8ed40c9ef..4a06783c0 100644 --- a/console/src/app/modules/idp-table/idp-table.component.scss +++ b/console/src/app/modules/idp-table/idp-table.component.scss @@ -26,11 +26,11 @@ } &.dark { - display: if($is-dark-theme, block, none); + display: if(sass($is-dark-theme): block; else: none); } &.light { - display: if($is-dark-theme, none, block); + display: if(sass($is-dark-theme): none; else: block); } } diff --git a/console/src/app/modules/info-overlay/info-overlay.component.scss b/console/src/app/modules/info-overlay/info-overlay.component.scss index 30c7c7329..81db85bb8 100644 --- a/console/src/app/modules/info-overlay/info-overlay.component.scss +++ b/console/src/app/modules/info-overlay/info-overlay.component.scss @@ -12,7 +12,7 @@ $back: map-get($background, background); .cnsl-overlay-backdrop { - background-color: if($is-dark-theme, #00000080, #ffffff80); + background-color: if(sass($is-dark-theme): #00000080; else: #ffffff80); backdrop-filter: blur(2px); } @@ -25,7 +25,7 @@ padding: 1rem; font-size: 14px; width: 300px; - border: 1px solid if($is-dark-theme, rgba(#8795a1, 0.2), rgba(#8795a1, 0.2)); + border: 1px solid if(sass($is-dark-theme): rgba(#8795a1, 0.2) ; else: rgba(#8795a1, 0.2)); &::before { visibility: hidden; diff --git a/console/src/app/modules/info-row/info-row.component.html b/console/src/app/modules/info-row/info-row.component.html index 17ef0dcee..0804c47c0 100644 --- a/console/src/app/modules/info-row/info-row.component.html +++ b/console/src/app/modules/info-row/info-row.component.html @@ -2,7 +2,7 @@

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

- @if (user?.state) { + @if (user.state) {

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

- @if (instance?.state) { + @if (instance.state) {

{{ 'INSTANCEID' | translate }}

- @if (instance?.id) { + @if (instance.id) {

{{ instance.id }}

}

{{ 'NAME' | translate }}

- @if (instance?.name) { + @if (instance.name) {

{{ instance.name }}

}

{{ 'VERSION' | translate }}

- @if (instance?.version) { + @if (instance.version) {

{{ instance.version }}

}

{{ 'IAM.PAGES.DOMAINLIST' | translate }}

- @for (domain of instance?.domainsList; track domain) { + @for (domain of instance.domainsList; track domain) {
- } -
- } - @if (app.apiConfig?.clientId) { -
- @if (app.apiConfig?.clientId; as clientId) { - - } -
- } -
- } +
+

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

+ @if (app.oidcConfig?.clientId) { +
+ @if (app.oidcConfig?.clientId; as clientId) { + + } +
+ } + @if (app.apiConfig?.clientId) { +
+ @if (app.apiConfig?.clientId; as clientId) { + + } +
+ } +
} diff --git a/console/src/app/modules/keyboard-shortcuts/keyboard-shortcuts.component.scss b/console/src/app/modules/keyboard-shortcuts/keyboard-shortcuts.component.scss index 1df9a9d57..b7254b1c3 100644 --- a/console/src/app/modules/keyboard-shortcuts/keyboard-shortcuts.component.scss +++ b/console/src/app/modules/keyboard-shortcuts/keyboard-shortcuts.component.scss @@ -104,7 +104,7 @@ } .keyboard-shortcuts-group { - $border-color: if($is-dark-theme, rgba(#8795a1, 0.2), rgba(#8795a1, 0.2)); + $border-color: if(sass($is-dark-theme): rgba(#8795a1, 0.2) ; else: rgba(#8795a1, 0.2)); border: 1px solid $border-color; background-color: $card-background-color; @@ -128,7 +128,7 @@ .keyboard-shortcuts-action-key { .keyboard-shortcuts-key-overlay { - background: if($is-dark-theme, #fff, #000); + background: if(sass($is-dark-theme): #fff; else: #000); } } } diff --git a/console/src/app/modules/label/label.component.scss b/console/src/app/modules/label/label.component.scss index c116b38f3..e6e50bdbf 100644 --- a/console/src/app/modules/label/label.component.scss +++ b/console/src/app/modules/label/label.component.scss @@ -26,7 +26,7 @@ .cnsl-form-field-disabled { .cnsl-label-wrapper { - color: if($is-dark-theme, #ffffff80, #00000061); + color: if(sass($is-dark-theme): #ffffff80; else: #00000061); } } diff --git a/console/src/app/modules/nav-toggle/nav-toggle.component.scss b/console/src/app/modules/nav-toggle/nav-toggle.component.scss index d69e6c497..5482347f4 100644 --- a/console/src/app/modules/nav-toggle/nav-toggle.component.scss +++ b/console/src/app/modules/nav-toggle/nav-toggle.component.scss @@ -51,7 +51,7 @@ padding: 2px; margin: -2px -9px -2px 8px; margin-left: 6px; - background-color: if($is-dark-theme, #ffffff20, #00000020); + background-color: if(sass($is-dark-theme): #ffffff20; else: #00000020); line-height: 18px; font-size: 11px; transition: background 0.2s ease; @@ -69,7 +69,7 @@ } &:hover { - background: if($is-dark-theme, #ffffff40, #00000010); + background: if(sass($is-dark-theme): #ffffff40; else: #00000010); } &.active { @@ -78,7 +78,7 @@ .c_label { .count { - background-color: if($is-dark-theme, #00000020, #ffffff20); + background-color: if(sass($is-dark-theme): #00000020; else: #ffffff20); } } } diff --git a/console/src/app/modules/nav/nav.component.scss b/console/src/app/modules/nav/nav.component.scss index 18a809b0a..345b620d0 100644 --- a/console/src/app/modules/nav/nav.component.scss +++ b/console/src/app/modules/nav/nav.component.scss @@ -82,7 +82,7 @@ padding: 2px; margin: -2px -9px -2px 8px; margin-left: 6px; - background-color: if($is-dark-theme, #ffffff20, #00000020); + background-color: if(sass($is-dark-theme): #ffffff20; else: #00000020); line-height: 18px; font-size: 11px; transition: background 0.2s ease; @@ -100,7 +100,7 @@ } &:hover { - background: if($is-dark-theme, #ffffff40, #00000010); + background: if(sass($is-dark-theme): #ffffff40; else: #00000010); } &.active { @@ -110,7 +110,7 @@ .c_label { .count { display: inline-block; - background-color: if($is-dark-theme, #00000020, #ffffff20); + background-color: if(sass($is-dark-theme): #00000020; else: #ffffff20); } } } @@ -136,7 +136,7 @@ transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1); .slash { - color: if($is-dark-theme, #525454, #d1d5d9); + color: if(sass($is-dark-theme): #525454; else: #d1d5d9); } .org-button { @@ -185,7 +185,7 @@ margin-right: 0.5rem; width: 100px; min-width: 50px; - background: if($is-dark-theme, #ffffff20, #00000010); + background: if(sass($is-dark-theme): #ffffff20; else: #00000010); border: none; padding: 2px; border-radius: 50vw; @@ -193,7 +193,7 @@ display: flex; &:hover { - background: if($is-dark-theme, #ffffff, $primary-color); + background: if(sass($is-dark-theme): #ffffff; else: $primary-color); transition: background ease 0.15s; } @@ -227,7 +227,7 @@ right: 0; bottom: 0; left: 0; - background: if($is-dark-theme, #fff, #000); + background: if(sass($is-dark-theme): #fff; else: #000); opacity: 0.15; border-radius: 4px; } diff --git a/console/src/app/modules/onboarding-card/onboarding-card.component.scss b/console/src/app/modules/onboarding-card/onboarding-card.component.scss index b70226075..f740b04ca 100644 --- a/console/src/app/modules/onboarding-card/onboarding-card.component.scss +++ b/console/src/app/modules/onboarding-card/onboarding-card.component.scss @@ -70,7 +70,7 @@ width: 1.5rem; border-radius: 50vw; margin-right: 1rem; - background-color: if($is-dark-theme, map-get($background, state), #e4e7e4); + background-color: if(sass($is-dark-theme): map-get($background, state) ; else: #e4e7e4); box-shadow: 0 0 3px #0000001a; border: 1px solid rgba(#8795a1, 0.2); diff --git a/console/src/app/modules/onboarding/onboarding.component.scss b/console/src/app/modules/onboarding/onboarding.component.scss index ac35ea6d0..4c1a92a26 100644 --- a/console/src/app/modules/onboarding/onboarding.component.scss +++ b/console/src/app/modules/onboarding/onboarding.component.scss @@ -13,8 +13,8 @@ $list-background-color: map-get($background, 300); $card-background-color: map-get($background, cards); - $border-color: if($is-dark-theme, rgba(#8795a1, 0.2), rgba(#8795a1, 0.2)); - $border-selected-color: if($is-dark-theme, #fff, #000); + $border-color: if(sass($is-dark-theme): rgba(#8795a1, 0.2) ; else: rgba(#8795a1, 0.2)); + $border-selected-color: if(sass($is-dark-theme): #fff; else: #000); .onboarding-header { display: flex; @@ -141,7 +141,7 @@ width: 1.5rem; border-radius: 50vw; margin-right: 1rem; - background-color: if($is-dark-theme, map-get($background, state), #e4e7e4); + background-color: if(sass($is-dark-theme): map-get($background, state) ; else: #e4e7e4); box-shadow: 0 0 3px #0000001a; border: 1px solid rgba(#8795a1, 0.2); diff --git a/console/src/app/modules/policies/idp-settings/idp-settings.component.scss b/console/src/app/modules/policies/idp-settings/idp-settings.component.scss index c9a5d11e8..61316ff02 100644 --- a/console/src/app/modules/policies/idp-settings/idp-settings.component.scss +++ b/console/src/app/modules/policies/idp-settings/idp-settings.component.scss @@ -87,11 +87,11 @@ } &.dark { - display: if($is-dark-theme, block, none); + display: if(sass($is-dark-theme): block; else: none); } &.light { - display: if($is-dark-theme, none, block); + display: if(sass($is-dark-theme): none; else: block); } } diff --git a/console/src/app/modules/policies/notification-smtp-provider/notification-smtp-provider.component.scss b/console/src/app/modules/policies/notification-smtp-provider/notification-smtp-provider.component.scss index 55c44e74b..38795bee8 100644 --- a/console/src/app/modules/policies/notification-smtp-provider/notification-smtp-provider.component.scss +++ b/console/src/app/modules/policies/notification-smtp-provider/notification-smtp-provider.component.scss @@ -69,11 +69,11 @@ } &.dark { - display: if($is-dark-theme, block, none); + display: if(sass($is-dark-theme): block; else: none); } &.light { - display: if($is-dark-theme, none, block); + display: if(sass($is-dark-theme): none; else: block); } } diff --git a/console/src/app/modules/policies/private-labeling-policy/preview/preview.component.scss b/console/src/app/modules/policies/private-labeling-policy/preview/preview.component.scss index 1970b00a4..b8d215711 100644 --- a/console/src/app/modules/policies/private-labeling-policy/preview/preview.component.scss +++ b/console/src/app/modules/policies/private-labeling-policy/preview/preview.component.scss @@ -2,7 +2,7 @@ @use '../../../label/label.component.scss' as label; @mixin preview-theme($show-dark) { - $theme: if($show-dark, $caos-dark-app-theme, $caos-light-app-theme); + $theme: if(sass($show-dark): $caos-dark-app-theme; else: $caos-light-app-theme); @include input.input-theme($theme); @include label.cnsl-label-theme($theme); diff --git a/console/src/app/modules/policies/private-labeling-policy/private-labeling-policy.component.scss b/console/src/app/modules/policies/private-labeling-policy/private-labeling-policy.component.scss index 6713b223e..987245a92 100644 --- a/console/src/app/modules/policies/private-labeling-policy/private-labeling-policy.component.scss +++ b/console/src/app/modules/policies/private-labeling-policy/private-labeling-policy.component.scss @@ -7,8 +7,8 @@ $is-dark-theme: map.get($theme, is-dark); $background: map.get($theme, background); $foreground: map.get($theme, foreground); - $p-border-color: if($is-dark-theme, rgba(#8795a1, 0.2), rgba(#8795a1, 0.2)); - $border-color: if($is-dark-theme, rgba(#8795a1, 0.2), rgba(#8795a1, 0.2)); + $p-border-color: if(sass($is-dark-theme): rgba(#8795a1, 0.2) ; else: rgba(#8795a1, 0.2)); + $border-color: if(sass($is-dark-theme): rgba(#8795a1, 0.2) ; else: rgba(#8795a1, 0.2)); .privatelabeling-policy { .header { diff --git a/console/src/app/modules/project-role-chip/project-role-chip.component.scss b/console/src/app/modules/project-role-chip/project-role-chip.component.scss index ca030e8d6..ec3372c5c 100644 --- a/console/src/app/modules/project-role-chip/project-role-chip.component.scss +++ b/console/src/app/modules/project-role-chip/project-role-chip.component.scss @@ -13,8 +13,8 @@ $list-background-color: map-get($background, 300); $card-background-color: map-get($background, cards); - $border-color: if($is-dark-theme, rgba(#8795a1, 0.2), rgba(#8795a1, 0.2)); - $border-selected-color: if($is-dark-theme, #fff, #000); + $border-color: if(sass($is-dark-theme): rgba(#8795a1, 0.2) ; else: rgba(#8795a1, 0.2)); + $border-selected-color: if(sass($is-dark-theme): #fff; else: #000); .project-role-chip { display: flex; diff --git a/console/src/app/modules/project-roles-table/project-roles-table.component.html b/console/src/app/modules/project-roles-table/project-roles-table.component.html index 1e2ae7e21..66dd540a9 100644 --- a/console/src/app/modules/project-roles-table/project-roles-table.component.html +++ b/console/src/app/modules/project-roles-table/project-roles-table.component.html @@ -118,7 +118,7 @@
- @if ((dataSource.loading$ | async) === false && !dataSource?.totalResult) { + @if ((dataSource.loading$ | async) === false && !dataSource.totalResult) {
{{ 'PROJECT.ROLE.EMPTY' | translate }} diff --git a/console/src/app/modules/providers/providers.scss b/console/src/app/modules/providers/providers.scss index 7baffada8..8964d5277 100644 --- a/console/src/app/modules/providers/providers.scss +++ b/console/src/app/modules/providers/providers.scss @@ -25,11 +25,11 @@ } &.dark { - display: if($is-dark-theme, block, none); + display: if(sass($is-dark-theme): block; else: none); } &.light { - display: if($is-dark-theme, none, block); + display: if(sass($is-dark-theme): none; else: block); } } diff --git a/console/src/app/modules/search-org-autocomplete/search-org-autocomplete.component.html b/console/src/app/modules/search-org-autocomplete/search-org-autocomplete.component.html index 6c9e1378d..348ceb065 100644 --- a/console/src/app/modules/search-org-autocomplete/search-org-autocomplete.component.html +++ b/console/src/app/modules/search-org-autocomplete/search-org-autocomplete.component.html @@ -1,36 +1,57 @@
- + {{ 'PROJECT.GRANT.CREATE.SEL_ORG_FORMFIELD' | translate }} - - - - @if (isLoading) { - - - + + + @if (query.isFetching()) { + + + } - @for (org of filteredOrgs; track org) { - -
-
- {{ org.name }} - - {{ org.primaryDomain }} -
-
-
- } -
- - {{ 'PROJECT.GRANT.CREATE.SEL_ORG_DESC' | translate }} + +
+ @for (page of query.data()?.pages; track $index) { + @for (org of page.result; track org.id) { + +
+ {{ org.name }} + {{ + org.primaryDomain + }} +
+
+ } + } +
+
diff --git a/console/src/app/modules/search-org-autocomplete/search-org-autocomplete.component.scss b/console/src/app/modules/search-org-autocomplete/search-org-autocomplete.component.scss deleted file mode 100644 index dbf3f4af1..000000000 --- a/console/src/app/modules/search-org-autocomplete/search-org-autocomplete.component.scss +++ /dev/null @@ -1,38 +0,0 @@ -.full-width { - width: 100%; -} - -input { - max-width: 500px; -} - -.org-option { - display: flex; - align-items: center; - - .org-option-column { - display: flex; - flex-direction: column; - align-items: flex-start; - justify-content: center; - width: 100%; - - span { - line-height: normal; - } - - .fill-space { - flex: 1; - } - - .smaller { - font-size: 13px; - } - } -} - -.org-autocomplete-target-desc { - font-size: 14px; - display: block; - margin-top: 0.5rem; -} diff --git a/console/src/app/modules/search-org-autocomplete/search-org-autocomplete.component.spec.ts b/console/src/app/modules/search-org-autocomplete/search-org-autocomplete.component.spec.ts deleted file mode 100644 index 03a2bda40..000000000 --- a/console/src/app/modules/search-org-autocomplete/search-org-autocomplete.component.spec.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; - -import { SearchOrgAutocompleteComponent } from './search-org-autocomplete.component'; - -describe('SearchOrgComponent', () => { - let component: SearchOrgAutocompleteComponent; - let fixture: ComponentFixture; - - beforeEach(waitForAsync(() => { - TestBed.configureTestingModule({ - declarations: [SearchOrgAutocompleteComponent], - }).compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(SearchOrgAutocompleteComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/console/src/app/modules/search-org-autocomplete/search-org-autocomplete.component.ts b/console/src/app/modules/search-org-autocomplete/search-org-autocomplete.component.ts index d7dbc6128..45ace06ff 100644 --- a/console/src/app/modules/search-org-autocomplete/search-org-autocomplete.component.ts +++ b/console/src/app/modules/search-org-autocomplete/search-org-autocomplete.component.ts @@ -1,85 +1,135 @@ -import { Component, EventEmitter, OnDestroy, OnInit, Output, ViewChild } from '@angular/core'; -import { UntypedFormControl } from '@angular/forms'; -import { MatAutocomplete, MatAutocompleteSelectedEvent } from '@angular/material/autocomplete'; -import { debounceTime, from, map, Subject, switchMap, takeUntil, tap } from 'rxjs'; -import { TextQueryMethod } from 'src/app/proto/generated/zitadel/object_pb'; -import { Org, OrgNameQuery, OrgQuery, OrgState, OrgStateQuery } from 'src/app/proto/generated/zitadel/org_pb'; -import { AuthenticationService } from 'src/app/services/authentication.service'; -import { GrpcAuthService } from 'src/app/services/grpc-auth.service'; +import { ChangeDetectionStrategy, Component, effect, inject, output, signal } from '@angular/core'; +import { NewAuthService } from 'src/app/services/new-auth.service'; +import { injectInfiniteQuery, keepPreviousData } from '@tanstack/angular-query-experimental'; +import { UserService } from 'src/app/services/user.service'; +import { AuthService, ListMyProjectOrgsRequestSchema } from '@zitadel/proto/zitadel/auth_pb'; +import { MatAutocompleteModule } from '@angular/material/autocomplete'; +import { FormControl, FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { MatInputModule } from '@angular/material/input'; +import { InputModule } from '../input/input.module'; +import { TranslatePipe } from '@ngx-translate/core'; +import { TextQueryMethod } from '@zitadel/proto/zitadel/object_pb'; +import { ScrollingModule } from '@angular/cdk/scrolling'; +import { Org, OrgFieldName, OrgState } from '@zitadel/proto/zitadel/org_pb'; +import { MessageInitShape } from '@bufbuild/protobuf'; +import { MatTooltip } from '@angular/material/tooltip'; +import { requiredValidator } from '../form-field/validators/validators'; +import { ScrollableDirective } from 'src/app/directives/scrollable/scrollable.directive'; +import { toObservable, toSignal } from '@angular/core/rxjs-interop'; +import { debounceTime } from 'rxjs'; +import { MatProgressSpinner } from '@angular/material/progress-spinner'; +import { ToastService } from 'src/app/services/toast.service'; @Component({ selector: 'cnsl-search-org-autocomplete', templateUrl: './search-org-autocomplete.component.html', - styleUrls: ['./search-org-autocomplete.component.scss'], - standalone: false, + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + ScrollingModule, + MatInputModule, + MatAutocompleteModule, + ReactiveFormsModule, + InputModule, + TranslatePipe, + FormsModule, + MatTooltip, + ScrollableDirective, + MatProgressSpinner, + ], }) -export class SearchOrgAutocompleteComponent implements OnInit, OnDestroy { - public myControl: UntypedFormControl = new UntypedFormControl(); - public filteredOrgs: Array = []; - public isLoading: boolean = false; - @ViewChild('auto') public matAutocomplete!: MatAutocomplete; - @Output() public selectionChanged: EventEmitter = new EventEmitter(); +export class SearchOrgAutocompleteComponent { + public readonly selectionChanged = output(); - private unsubscribed$: Subject = new Subject(); - constructor( - public authService: AuthenticationService, - private auth: GrpcAuthService, - ) { - this.myControl.valueChanges - .pipe( - takeUntil(this.unsubscribed$), - debounceTime(200), - tap(() => (this.isLoading = true)), - switchMap((value) => { - const stateQuery = new OrgQuery(); - const orgStateQuery = new OrgStateQuery(); - orgStateQuery.setState(OrgState.ORG_STATE_ACTIVE); - stateQuery.setStateQuery(orgStateQuery); + protected readonly searchControl = new FormControl(null, { validators: [requiredValidator] }); + protected readonly searchSignal = signal(''); + protected readonly debouncedSearchSignal = toSignal(toObservable(this.searchSignal).pipe(debounceTime(250)), { + initialValue: this.searchSignal(), + }); - let queries: OrgQuery[] = [stateQuery]; + private readonly authService = inject(NewAuthService); + private readonly userService = inject(UserService); - if (value) { - const nameQuery = new OrgQuery(); - const orgNameQuery = new OrgNameQuery(); - orgNameQuery.setName(value); - orgNameQuery.setMethod(TextQueryMethod.TEXT_QUERY_METHOD_CONTAINS_IGNORE_CASE); - nameQuery.setNameQuery(orgNameQuery); - queries = [stateQuery, nameQuery]; - } + protected readonly query = injectInfiniteQuery(() => { + const search = this.debouncedSearchSignal(); - return from(this.auth.listMyProjectOrgs(undefined, 0, queries)).pipe( - map((resp) => { - return resp.resultList.sort((left, right) => left.name.localeCompare(right.name)); - }), - ); - }), - ) - .subscribe((returnValue) => { - this.isLoading = false; - this.filteredOrgs = returnValue; - }); - } + const stateQuery = [ + { + query: { + case: 'stateQuery', + value: { + state: OrgState.ACTIVE, + }, + }, + }, + ] satisfies MessageInitShape['queries']; - public ngOnInit(): void { - const query = new OrgQuery(); - const orgStateQuery = new OrgStateQuery(); - orgStateQuery.setState(OrgState.ORG_STATE_ACTIVE); - query.setStateQuery(orgStateQuery); + return { + queryKey: [this.userService.userId(), AuthService.name, AuthService.method.listMyProjectOrgs.name, 'infinite', search], + placeholderData: keepPreviousData, + queryFn: async ({ pageParam, signal }) => + this.authService.listMyProjectOrgs( + { + query: { + limit: 20, + offset: BigInt(pageParam.offset), + }, + sortingColumn: OrgFieldName.NAME, + queries: search + ? [ + ...stateQuery, + { + query: { + case: 'nameQuery', + value: { + name: search, + method: TextQueryMethod.CONTAINS_IGNORE_CASE, + }, + }, + }, + ] + : stateQuery, + }, + signal, + ), + initialPageParam: { + offset: 0, + }, + getNextPageParam: (lastPage, allPages, { offset }) => { + const nextPageParam = { offset: offset + lastPage.result.length }; + const loadedCount = allPages.reduce((count, page) => count + page.result.length, 0); - this.auth.listMyProjectOrgs(undefined, 0, [query]).then((orgs) => { - this.filteredOrgs = orgs.resultList; + if (loadedCount < lastPage.details!.totalResult) { + return nextPageParam; + } + + return undefined; + }, + }; + }); + + // used to only open matTooltip if the text gets truncated + protected readonly showTooltip = signal(false); + + constructor() { + const toastService = inject(ToastService); + effect(() => { + const error = this.query.error(); + if (error) { + toastService.showError(error); + } }); } - public ngOnDestroy(): void { - this.unsubscribed$.next(); + protected async onScroll(position: 'bottom' | 'top') { + if (this.query.isFetchingNextPage() || !this.query.hasNextPage() || position === 'top') { + return; + } + + await this.query.fetchNextPage(); } - public displayFn(org?: Org.AsObject): string { - return org ? `${org.name}` : ''; - } - - public selected(event: MatAutocompleteSelectedEvent): void { - this.selectionChanged.emit(event.option.value); + protected displayOrg(org: Org | null) { + return org?.name ?? ''; } } diff --git a/console/src/app/modules/search-org-autocomplete/search-org-autocomplete.module.ts b/console/src/app/modules/search-org-autocomplete/search-org-autocomplete.module.ts deleted file mode 100644 index 26bf94292..000000000 --- a/console/src/app/modules/search-org-autocomplete/search-org-autocomplete.module.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { MatAutocompleteModule } from '@angular/material/autocomplete'; -import { MatButtonModule } from '@angular/material/button'; -import { MatChipsModule } from '@angular/material/chips'; -import { MatIconModule } from '@angular/material/icon'; -import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; -import { MatSelectModule } from '@angular/material/select'; -import { TranslateModule } from '@ngx-translate/core'; -import { InputModule } from 'src/app/modules/input/input.module'; - -import { SearchOrgAutocompleteComponent } from './search-org-autocomplete.component'; - -@NgModule({ - declarations: [SearchOrgAutocompleteComponent], - imports: [ - CommonModule, - MatAutocompleteModule, - MatChipsModule, - MatButtonModule, - InputModule, - MatIconModule, - ReactiveFormsModule, - MatProgressSpinnerModule, - FormsModule, - TranslateModule, - MatSelectModule, - ], - exports: [SearchOrgAutocompleteComponent], -}) -export class SearchOrgAutocompleteModule {} diff --git a/console/src/app/modules/search-user-autocomplete/search-user-autocomplete.component.scss b/console/src/app/modules/search-user-autocomplete/search-user-autocomplete.component.scss index 4f2b91883..aff81f7ad 100644 --- a/console/src/app/modules/search-user-autocomplete/search-user-autocomplete.component.scss +++ b/console/src/app/modules/search-user-autocomplete/search-user-autocomplete.component.scss @@ -9,8 +9,8 @@ $secondary-text: map-get($foreground, secondary-text); $is-dark-theme: map-get($theme, is-dark); - $link-hover-color: if($is-dark-theme, map-get($primary, 200), $primary-color); - $link-color: if($is-dark-theme, $lighter-primary-color, $primary-color); + $link-hover-color: if(sass($is-dark-theme): map-get($primary, 200) ; else: $primary-color); + $link-color: if(sass($is-dark-theme): $lighter-primary-color; else: $primary-color); .user-autocomplete-found { margin: 0.5rem 0; diff --git a/console/src/app/modules/shortcuts/shortcuts.component.scss b/console/src/app/modules/shortcuts/shortcuts.component.scss index 5dce51813..4243880d8 100644 --- a/console/src/app/modules/shortcuts/shortcuts.component.scss +++ b/console/src/app/modules/shortcuts/shortcuts.component.scss @@ -13,8 +13,8 @@ $list-background-color: map-get($background, 300); $card-background-color: map-get($background, cards); - $border-color: if($is-dark-theme, rgba(#8795a1, 0.2), rgba(#8795a1, 0.2)); - $border-selected-color: if($is-dark-theme, #fff, #000); + $border-color: if(sass($is-dark-theme): rgba(#8795a1, 0.2) ; else: rgba(#8795a1, 0.2)); + $border-selected-color: if(sass($is-dark-theme): #fff; else: #000); .shortcut-container { width: 100%; diff --git a/console/src/app/modules/sidenav/sidenav.component.scss b/console/src/app/modules/sidenav/sidenav.component.scss index bb55a6999..78a0ade17 100644 --- a/console/src/app/modules/sidenav/sidenav.component.scss +++ b/console/src/app/modules/sidenav/sidenav.component.scss @@ -32,12 +32,12 @@ font-size: 11px; letter-spacing: 0.05em; text-transform: uppercase; - color: if($is-dark-theme, #ffffff60, #00000060); + color: if(sass($is-dark-theme): #ffffff60; else: #00000060); margin-top: 1rem; font-weight: 600; margin-bottom: 0.5rem; padding-top: 1rem; - border-top: 1px solid if($is-dark-theme, rgba(#8795a1, 0.2), rgba(#8795a1, 0.2)); + border-top: 1px solid if(sass($is-dark-theme): rgba(#8795a1, 0.2) ; else: rgba(#8795a1, 0.2)); &:nth-child(2) { border-top: none; diff --git a/console/src/app/modules/smtp-table/smtp-table.component.html b/console/src/app/modules/smtp-table/smtp-table.component.html index 912d0f301..14df27d32 100644 --- a/console/src/app/modules/smtp-table/smtp-table.component.html +++ b/console/src/app/modules/smtp-table/smtp-table.component.html @@ -131,7 +131,7 @@
- @if ((loading$ | async) === false && !dataSource?.data?.length) { + @if ((loading$ | async) === false && !dataSource.data.length) {
{{ 'SMTP.LIST.EMPTY' | translate }} @@ -139,7 +139,6 @@ }
- @if ((dataSource.loading$ | async) === false && !dataSource?.totalResult) { + @if ((dataSource.loading$ | async) === false && !dataSource.totalResult) {
{{ 'GRANTS.EMPTY' | translate }} diff --git a/console/src/app/pages/home/home.component.scss b/console/src/app/pages/home/home.component.scss index eafcbcdd5..96ed721cc 100644 --- a/console/src/app/pages/home/home.component.scss +++ b/console/src/app/pages/home/home.component.scss @@ -13,8 +13,8 @@ $list-background-color: map-get($background, 300); $card-background-color: map-get($background, cards); - $border-color: if($is-dark-theme, rgba(#8795a1, 0.2), rgba(#8795a1, 0.2)); - $border-selected-color: if($is-dark-theme, #fff, #000); + $border-color: if(sass($is-dark-theme): rgba(#8795a1, 0.2) ; else: rgba(#8795a1, 0.2)); + $border-selected-color: if(sass($is-dark-theme): #fff; else: #000); .home-title { font-size: 2rem; diff --git a/console/src/app/pages/org-actions/actions.component.html b/console/src/app/pages/org-actions/actions.component.html index 20cb8a7b6..4eaeb53e7 100644 --- a/console/src/app/pages/org-actions/actions.component.html +++ b/console/src/app/pages/org-actions/actions.component.html @@ -50,7 +50,7 @@
{{ flow.type?.name?.localizedMessage }} - @if (flow.type && (flow.triggerActionsList?.length ?? 0) > 0) { + @if (flow.type && flow.triggerActionsList.length > 0) {
-
+
@for (action of trigger.actionsList; track action) {
diff --git a/console/src/app/pages/org-actions/actions.component.ts b/console/src/app/pages/org-actions/actions.component.ts index 937e1a214..420a06325 100644 --- a/console/src/app/pages/org-actions/actions.component.ts +++ b/console/src/app/pages/org-actions/actions.component.ts @@ -21,7 +21,7 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; standalone: false, }) export class ActionsComponent { - protected flow!: Flow.AsObject; + protected flow?: Flow.AsObject; protected typeControl: UntypedFormControl = new UntypedFormControl(); @@ -124,23 +124,23 @@ export class ActionsComponent { }); } - drop(triggerActionsListIndex: number, array: any[], event: CdkDragDrop) { + drop(flow: Flow.AsObject, triggerActionsListIndex: number, array: any[], event: CdkDragDrop) { moveItemInArray(array, event.previousIndex, event.currentIndex); - this.saveFlow(triggerActionsListIndex); + this.saveFlow(flow, triggerActionsListIndex); } - saveFlow(index: number) { + saveFlow(flow: Flow.AsObject, index: number) { if ( - this.flow.type && - this.flow.triggerActionsList && - this.flow.triggerActionsList[index] && - this.flow.triggerActionsList[index]?.triggerType + flow.type && + flow.triggerActionsList && + flow.triggerActionsList[index] && + flow.triggerActionsList[index]?.triggerType ) { this.mgmtService .setTriggerActions( - this.flow.triggerActionsList[index].actionsList.map((action) => action.id), - this.flow.type.id, - this.flow.triggerActionsList[index].triggerType?.id ?? '', + flow.triggerActionsList[index].actionsList.map((action) => action.id), + flow.type.id, + flow.triggerActionsList[index].triggerType?.id ?? '', ) .then(() => { this.toast.showInfo('FLOWS.TOAST.ACTIONSSET', true); @@ -151,8 +151,8 @@ export class ActionsComponent { } } - protected removeTriggerActionsList(index: number) { - if (this.flow.type && this.flow.triggerActionsList && this.flow.triggerActionsList[index]) { + protected removeTriggerActionsList(flow: Flow.AsObject, index: number) { + if (flow.type && flow.triggerActionsList && flow.triggerActionsList[index]) { const dialogRef = this.dialog.open(WarnDialogComponent, { data: { confirmKey: 'ACTIONS.CLEAR', @@ -166,10 +166,10 @@ export class ActionsComponent { dialogRef.afterClosed().subscribe((resp) => { if (resp) { this.mgmtService - .setTriggerActions([], this.flow?.type?.id ?? '', this.flow.triggerActionsList[index].triggerType?.id ?? '') + .setTriggerActions([], flow?.type?.id ?? '', flow.triggerActionsList[index].triggerType?.id ?? '') .then(() => { this.toast.showInfo('FLOWS.TOAST.ACTIONSSET', true); - this.loadFlow(this.flow?.type?.id ?? ''); + this.loadFlow(flow?.type?.id ?? ''); }) .catch((error) => { this.toast.showError(error); diff --git a/console/src/app/pages/projects/apps/app-detail/app-detail.component.html b/console/src/app/pages/projects/apps/app-detail/app-detail.component.html index 24875bd04..5a4c35273 100644 --- a/console/src/app/pages/projects/apps/app-detail/app-detail.component.html +++ b/console/src/app/pages/projects/apps/app-detail/app-detail.component.html @@ -68,12 +68,12 @@ } - @if (app && app.oidcConfig && app.oidcConfig.complianceProblemsList && app.oidcConfig.complianceProblemsList?.length) { + @if (app && app.oidcConfig && app.oidcConfig.complianceProblemsList && app.oidcConfig.complianceProblemsList.length) {

{{ 'APP.COMPLIANCE' | translate }}

    - @for (problem of app.oidcConfig?.complianceProblemsList || []; track problem) { + @for (problem of app.oidcConfig.complianceProblemsList; track problem) {
  • {{ problem.localizedMessage }}
  • @@ -444,7 +444,7 @@ @if (currentSetting.id === 'additional-origins') {

    {{ 'APP.ADDITIONALORIGINSDESC' | translate }}

    - @if (additionalOriginsList && appType && appType?.value !== undefined) { + @if (additionalOriginsList && appType?.value !== undefined) { - @if (apiForm && app?.apiConfig) { + @if (apiForm && app.apiConfig) {
    - @if (grant?.state === ProjectGrantState.PROJECT_GRANT_STATE_ACTIVE) { - } - @if (grant?.state === ProjectGrantState.PROJECT_GRANT_STATE_INACTIVE) { - } @@ -48,8 +48,8 @@ } @@ -63,15 +63,15 @@ [canWrite]="['project.grant.member.write', 'project.grant.member.write:' + grant.grantId] | hasRole | async" [canDelete]="['project.grant.member.delete', 'project.grant.member.delete:' + grant.grantId] | hasRole | async" [memberRoleOptions]="memberRoleOptions" - (updateRoles)="updateMemberRoles($event.member, $event.change)" - (deleteMember)="removeProjectMember($event)" + (updateRoles)="updateMemberRoles(grant, $event.member, $event.change)" + (deleteMember)="removeProjectMember(grant, $event)" [factoryLoadFunc]="changePageFactory" (changedSelection)="selection = $event" [refreshTrigger]="changePage" >
@@ -92,13 +92,13 @@ [disabled]=" (['project.grant.member.write', 'project.grant.member.write:' + grant.grantId] | hasRole | async) === false " - (click)="openAddMember()" + (click)="openAddMember(grant)" color="primary" mat-raised-button >
add{{ 'ACTIONS.NEW' | translate }} - +
diff --git a/console/src/app/pages/projects/owned-projects/project-grant-detail/project-grant-detail.component.ts b/console/src/app/pages/projects/owned-projects/project-grant-detail/project-grant-detail.component.ts index ba3dac6e8..32a2f5e56 100644 --- a/console/src/app/pages/projects/owned-projects/project-grant-detail/project-grant-detail.component.ts +++ b/console/src/app/pages/projects/owned-projects/project-grant-detail/project-grant-detail.component.ts @@ -27,7 +27,7 @@ import { ProjectGrantMembersDataSource } from './project-grant-members-datasourc export class ProjectGrantDetailComponent { public INITIALPAGESIZE: number = 25; - public grant!: GrantedProject.AsObject; + public grant: GrantedProject.AsObject | undefined; public projectid: string = ''; public grantid: string = ''; @@ -99,13 +99,13 @@ export class ProjectGrantDetailComponent { }); } - public changeState(newState: ProjectGrantState): void { + public changeState(grant: GrantedProject.AsObject, newState: ProjectGrantState): void { if (newState === ProjectGrantState.PROJECT_GRANT_STATE_ACTIVE) { this.mgmtService .reactivateProjectGrant(this.grantid, this.projectid) .then(() => { this.toast.showInfo('PROJECT.TOAST.REACTIVATED', true); - this.grant.state = newState; + grant.state = newState; }) .catch((error) => { this.toast.showError(error); @@ -115,7 +115,7 @@ export class ProjectGrantDetailComponent { .deactivateProjectGrant(this.grantid, this.projectid) .then(() => { this.toast.showInfo('PROJECT.TOAST.DEACTIVATED', true); - this.grant.state = newState; + grant.state = newState; setTimeout(() => { this.changePage.emit(); }, 1000); @@ -144,56 +144,25 @@ export class ProjectGrantDetailComponent { }); } - updateRoles(selectionChange: string[]): void { - this.mgmtService - .updateProjectGrant(this.grant.grantId, this.grant.projectId, selectionChange) - .then(() => { - this.toast.showInfo('PROJECT.GRANT.TOAST.PROJECTGRANTUPDATED', true); - setTimeout(() => { - this.changePage.emit(); - }, 1000); - }) - .catch((error) => { - this.toast.showError(error); - this.changePage.emit(); - }); + public async removeProjectMemberSelection(grant: GrantedProject.AsObject) { + await Promise.all(this.selection.map((member) => this.removeProjectMember(grant, member))); } - public removeProjectMemberSelection(): void { - Promise.all( - this.selection.map((member) => { - return this.mgmtService - .removeProjectGrantMember(this.grant.projectId, this.grant.grantId, member.userId) - .then(() => { - this.toast.showInfo('PROJECT.GRANT.TOAST.PROJECTGRANTMEMBERREMOVED', true); - setTimeout(() => { - this.changePage.emit(); - }, 1000); - }) - .catch((error) => { - this.changePage.emit(); - this.toast.showError(error); - }); - }), - ); + public async removeProjectMember(grant: GrantedProject.AsObject, member: Member.AsObject) { + try { + await this.mgmtService.removeProjectGrantMember(grant.projectId, grant.grantId, member.userId); + + this.toast.showInfo('PROJECT.GRANT.TOAST.PROJECTGRANTMEMBERREMOVED', true); + + await new Promise((res) => setTimeout(res, 1000)); + } catch (error) { + this.toast.showError(error); + } finally { + this.changePage.emit(); + } } - public removeProjectMember(member: Member.AsObject): void { - this.mgmtService - .removeProjectGrantMember(this.grant.projectId, this.grant.grantId, member.userId) - .then(() => { - this.toast.showInfo('PROJECT.GRANT.TOAST.PROJECTGRANTMEMBERREMOVED', true); - setTimeout(() => { - this.changePage.emit(); - }, 1000); - }) - .catch((error) => { - this.changePage.emit(); - this.toast.showError(error); - }); - } - - public async openAddMember(): Promise { + public async openAddMember(grant: GrantedProject.AsObject): Promise { const dialogRef = this.dialog.open(MemberCreateDialogComponent, { data: { creationType: CreationType.PROJECT_GRANTED, @@ -210,7 +179,7 @@ export class ProjectGrantDetailComponent { const userIds = users.map((user) => user.id); Promise.all( userIds.map((userid: string) => { - return this.mgmtService.addProjectGrantMember(this.grant.projectId, this.grant.grantId, userid, resp.roles); + return this.mgmtService.addProjectGrantMember(grant.projectId, grant.grantId, userid, resp.roles); }), ) .then(() => { @@ -228,9 +197,9 @@ export class ProjectGrantDetailComponent { }); } - updateMemberRoles(member: Member.AsObject, selectionChange: string[]): void { + updateMemberRoles(grant: GrantedProject.AsObject, member: Member.AsObject, selectionChange: string[]): void { this.mgmtService - .updateProjectGrantMember(this.grant.projectId, this.grant.grantId, member.userId, selectionChange) + .updateProjectGrantMember(grant.projectId, grant.grantId, member.userId, selectionChange) .then(() => { setTimeout(() => { this.changePage.emit(); @@ -243,13 +212,13 @@ export class ProjectGrantDetailComponent { }); } - removeRole(role: string): void { - const index = this.grant.grantedRoleKeysList.findIndex((r) => r === role); + removeRole(grant: GrantedProject.AsObject, role: string): void { + const index = grant.grantedRoleKeysList.findIndex((r) => r === role); if (index > -1) { - this.grant.grantedRoleKeysList.splice(index, 1); + grant.grantedRoleKeysList.splice(index, 1); this.mgmtService - .updateProjectGrant(this.grant.grantId, this.grant.projectId, this.grant.grantedRoleKeysList) + .updateProjectGrant(grant.grantId, grant.projectId, grant.grantedRoleKeysList) .then(() => { setTimeout(() => { this.changePage.emit(); @@ -263,13 +232,13 @@ export class ProjectGrantDetailComponent { } } - public editRoles(): void { + public editRoles(grant: GrantedProject.AsObject): void { const dialogRef = this.dialog.open( UserGrantRoleDialogComponent, { data: { - projectId: this.grant.projectId, - selectedRoleKeysList: this.grant.grantedRoleKeysList, + projectId: grant.projectId, + selectedRoleKeysList: grant.grantedRoleKeysList, i18nTitle: 'PROJECT.GRANT.EDITTITLE', }, width: '600px', @@ -281,10 +250,10 @@ export class ProjectGrantDetailComponent { return; } this.mgmtService - .updateProjectGrant(this.grant.grantId, this.grant.projectId, resp.roles) + .updateProjectGrant(grant.grantId, grant.projectId, resp.roles) .then(() => { this.toast.showInfo('PROJECT.GRANT.TOAST.PROJECTGRANTUPDATED', true); - this.grant.grantedRoleKeysList = resp.roles; + grant.grantedRoleKeysList = resp.roles; setTimeout(() => { this.changePage.emit(); }, 1000); diff --git a/console/src/app/pages/projects/owned-projects/project-grant-detail/project-grant-illustration/project-grant-illustration.component.scss b/console/src/app/pages/projects/owned-projects/project-grant-detail/project-grant-illustration/project-grant-illustration.component.scss index af0c35d8c..fad12d5fa 100644 --- a/console/src/app/pages/projects/owned-projects/project-grant-detail/project-grant-illustration/project-grant-illustration.component.scss +++ b/console/src/app/pages/projects/owned-projects/project-grant-detail/project-grant-illustration/project-grant-illustration.component.scss @@ -1,9 +1,9 @@ @mixin project-grant-illustration-theme($theme) { $foreground: map-get($theme, foreground); $is-dark-theme: map-get($theme, is-dark); - $overlayColor: if($is-dark-theme, #ffffff10, #00000008); - $highlightedOverlayColor: if($is-dark-theme, #68c4cf14, #68c4cf14); - $borderColor: if($is-dark-theme, #ffffff20, #00000020); + $overlayColor: if(sass($is-dark-theme): #ffffff10; else: #00000008); + $highlightedOverlayColor: if(sass($is-dark-theme): #68c4cf14; else: #68c4cf14); + $borderColor: if(sass($is-dark-theme): #ffffff20; else: #00000020); .pgi { display: flex; diff --git a/console/src/app/pages/projects/owned-projects/project-grants/project-grants.component.html b/console/src/app/pages/projects/owned-projects/project-grants/project-grants.component.html index b3c438914..34bd5f18a 100644 --- a/console/src/app/pages/projects/owned-projects/project-grants/project-grants.component.html +++ b/console/src/app/pages/projects/owned-projects/project-grants/project-grants.component.html @@ -136,7 +136,7 @@
- @if ((dataSource.loading$ | async) === false && !dataSource?.totalResult) { + @if ((dataSource.loading$ | async) === false && !dataSource.totalResult) {
{{ 'PROJECT.GRANT.EMPTY' | translate }} diff --git a/console/src/app/pages/projects/project-list/project-list.component.html b/console/src/app/pages/projects/project-list/project-list.component.html index d7994338b..c6144a8e9 100644 --- a/console/src/app/pages/projects/project-list/project-list.component.html +++ b/console/src/app/pages/projects/project-list/project-list.component.html @@ -158,7 +158,7 @@
- @if ((loading$ | async) === false && !dataSource?.data?.length) { + @if ((loading$ | async) === false && !dataSource.data.length) {
{{ 'PROJECT.TABLE.EMPTY' | translate }} diff --git a/console/src/app/pages/users/user-detail/auth-user-detail/auth-passwordless/auth-passwordless.component.html b/console/src/app/pages/users/user-detail/auth-user-detail/auth-passwordless/auth-passwordless.component.html index 8e163ba5d..ad9cadf9e 100644 --- a/console/src/app/pages/users/user-detail/auth-user-detail/auth-passwordless/auth-passwordless.component.html +++ b/console/src/app/pages/users/user-detail/auth-user-detail/auth-passwordless/auth-passwordless.component.html @@ -73,7 +73,7 @@ - @if ((loading$ | async) === false && !dataSource?.data?.length) { + @if ((loading$ | async) === false && !dataSource.data.length) {
{{ 'USER.PASSWORDLESS.EMPTY' | translate }} diff --git a/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-mfa/auth-user-mfa.component.html b/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-mfa/auth-user-mfa.component.html index efcb66b82..cbe5b91f6 100644 --- a/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-mfa/auth-user-mfa.component.html +++ b/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-mfa/auth-user-mfa.component.html @@ -93,7 +93,7 @@ - @if ((loading$ | async) === false && !dataSource?.data?.length) { + @if ((loading$ | async) === false && !dataSource.data.length) {
{{ 'USER.MFA.EMPTY' | translate }} diff --git a/console/src/app/pages/users/user-detail/external-idps/external-idps.component.html b/console/src/app/pages/users/user-detail/external-idps/external-idps.component.html index 5d296aadb..8ecaa6ab5 100644 --- a/console/src/app/pages/users/user-detail/external-idps/external-idps.component.html +++ b/console/src/app/pages/users/user-detail/external-idps/external-idps.component.html @@ -74,7 +74,7 @@
- @if ((loading$ | async) === false && !dataSource?.data?.length) { + @if ((loading$ | async) === false && !dataSource.data.length) {
{{ 'USER.EXTERNALIDP.EMPTY' | translate }} diff --git a/console/src/app/pages/users/user-detail/user-detail/passwordless/passwordless.component.html b/console/src/app/pages/users/user-detail/user-detail/passwordless/passwordless.component.html index e100e18aa..5026ee090 100644 --- a/console/src/app/pages/users/user-detail/user-detail/passwordless/passwordless.component.html +++ b/console/src/app/pages/users/user-detail/user-detail/passwordless/passwordless.component.html @@ -72,7 +72,7 @@ - @if ((loading$ | async) === false && !dataSource?.data?.length) { + @if ((loading$ | async) === false && !dataSource.data.length) {
{{ 'USER.PASSWORDLESS.EMPTY' | translate }} diff --git a/console/src/app/pages/users/user-list/user-table/user-table.component.html b/console/src/app/pages/users/user-list/user-table/user-table.component.html index 2570aa353..2bb04b5b0 100644 --- a/console/src/app/pages/users/user-list/user-table/user-table.component.html +++ b/console/src/app/pages/users/user-list/user-table/user-table.component.html @@ -233,7 +233,7 @@ >
- @if (!loading() && !dataSource?.data?.length) { + @if (!loading() && !dataSource.data.length) {
{{ 'USER.TABLE.EMPTY' | translate }} diff --git a/console/src/app/services/new-auth.service.ts b/console/src/app/services/new-auth.service.ts index c0f552f52..c8b723b6c 100644 --- a/console/src/app/services/new-auth.service.ts +++ b/console/src/app/services/new-auth.service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@angular/core'; import { GrpcService } from './grpc.service'; -import { create } from '@bufbuild/protobuf'; +import { create, MessageInitShape } from '@bufbuild/protobuf'; import { AddMyAuthFactorOTPSMSResponse, GetMyLoginPolicyResponse, @@ -19,6 +19,7 @@ import { ListMyMetadataResponse, VerifyMyPhoneResponse, ListMyZitadelPermissionsResponse, + ListMyProjectOrgsRequestSchema, } from '@zitadel/proto/zitadel/auth_pb'; import { injectQuery } from '@tanstack/angular-query-experimental'; import { UserService } from './user.service'; @@ -82,4 +83,8 @@ export class NewAuthService { queryFn: () => this.listMyZitadelPermissions().then(({ result }) => result), })); } + + public listMyProjectOrgs(req: MessageInitShape, signal?: AbortSignal) { + return this.grpcService.authNew.listMyProjectOrgs(req, { signal }); + } } diff --git a/console/src/app/services/new-mgmt.service.ts b/console/src/app/services/new-mgmt.service.ts index 3847a6c20..0f59bc0bf 100644 --- a/console/src/app/services/new-mgmt.service.ts +++ b/console/src/app/services/new-mgmt.service.ts @@ -2,6 +2,7 @@ import { Injectable } from '@angular/core'; import { GrpcService } from './grpc.service'; import { AddOrgResponse, + AddProjectGrantRequestSchema, DeactivateOrgResponse, GenerateMachineSecretRequestSchema, GenerateMachineSecretResponse, @@ -123,4 +124,8 @@ export class NewMgmtService { public addOrg(name: string): Promise { return this.grpcService.mgmtNew.addOrg({ name }); } + + public addProjectGrant(req: MessageInitShape) { + return this.grpcService.mgmtNew.addProjectGrant(req); + } } diff --git a/console/src/styles/codemirror.scss b/console/src/styles/codemirror.scss index 78e0fac7e..94026c3c8 100644 --- a/console/src/styles/codemirror.scss +++ b/console/src/styles/codemirror.scss @@ -4,11 +4,11 @@ $is-dark-theme: map.get($theme, is-dark); .cm-s-material.CodeMirror { - background-color: if($is-dark-theme, #00000020, #263238); + background-color: if(sass($is-dark-theme): #00000020; else: #263238); color: #eff; font-size: 14px; border-radius: 4px; - border: 1px solid if($is-dark-theme, #403e3e, #00000040); + border: 1px solid if(sass($is-dark-theme): #403e3e; else: #00000040); } } diff --git a/console/src/styles/input.scss b/console/src/styles/input.scss index 1efb75a9a..7c557d8a9 100644 --- a/console/src/styles/input.scss +++ b/console/src/styles/input.scss @@ -20,8 +20,8 @@ transform: all 0.2 linear; font-size: 1rem; border: none; - border: 1px solid if($is-dark-theme, #f9f7f775, #1a191954); - background-color: if($is-dark-theme, #00000040, #00000004); + border: 1px solid if(sass($is-dark-theme): #f9f7f775; else: #1a191954); + background-color: if(sass($is-dark-theme): #00000040; else: #00000004); border-radius: 4px; height: 40px; padding: 10px; @@ -34,7 +34,7 @@ margin-bottom: 2px; &:hover { - border-color: if($is-dark-theme, #e0e0e0, #1a1b1b); + border-color: if(sass($is-dark-theme): #e0e0e0; else: #1a1b1b); } &:active, @@ -49,11 +49,11 @@ } &[disabled] { - border-color: if($is-dark-theme, #36373850, #cccdce50); - color: if($is-dark-theme, #ffffff80, #00000061); + border-color: if(sass($is-dark-theme): #36373850; else: #cccdce50); + color: if(sass($is-dark-theme): #ffffff80; else: #00000061); &::placeholder { - color: if($is-dark-theme, #ffffff80, #00000061); + color: if(sass($is-dark-theme): #ffffff80; else: #00000061); } } } diff --git a/console/src/styles/table.scss b/console/src/styles/table.scss index 7ae4edaf7..0f73a0348 100644 --- a/console/src/styles/table.scss +++ b/console/src/styles/table.scss @@ -88,7 +88,7 @@ } .no-content-row { - background-color: if($is-dark-theme, #ffffff10, #f4f4f470); + background-color: if(sass($is-dark-theme): #ffffff10; else: #f4f4f470); border-bottom-left-radius: 5px; border-bottom-right-radius: 5px; display: flex; @@ -123,41 +123,41 @@ font-weight: 500; font-size: 12px; white-space: nowrap; - background-color: if($is-dark-theme, map.get($background, state), #e4e7e4); + background-color: if(sass($is-dark-theme): map.get($background, state) ; else: #e4e7e4); box-shadow: 0 0 3px #0000001a; &.active { - background-color: if($is-dark-theme, map.get($background, state-active), #cbf4c9); - color: if($is-dark-theme, #cbf4c9, #0e6245); + background-color: if(sass($is-dark-theme): map.get($background, state-active) ; else: #cbf4c9); + color: if(sass($is-dark-theme): #cbf4c9; else: #0e6245); } &.inactive { - color: if($is-dark-theme, #ffc1c1, #620e0e); - background-color: if($is-dark-theme, map.get($background, state-inactive), #ffc1c1); + color: if(sass($is-dark-theme): #ffc1c1; else: #620e0e); + background-color: if(sass($is-dark-theme): map.get($background, state-inactive) ; else: #ffc1c1); } &.neutral { - background: if($is-dark-theme, #01489c78, #47a8ff82); - color: if($is-dark-theme, #47a8ff, #01489c); + background: if(sass($is-dark-theme): #01489c78; else: #47a8ff82); + color: if(sass($is-dark-theme): #47a8ff; else: #01489c); } } .bg-state { - background-color: if($is-dark-theme, map.get($background, state), #e4e7e4) !important; + background-color: if(sass($is-dark-theme): map.get($background, state) ; else: #e4e7e4) !important; &.active { - background-color: if($is-dark-theme, map.get($background, state-active), #cbf4c9) !important; - color: if($is-dark-theme, #cbf4c9, #0e6245) !important; + background-color: if(sass($is-dark-theme): map.get($background, state-active) ; else: #cbf4c9) !important; + color: if(sass($is-dark-theme): #cbf4c9; else: #0e6245) !important; } &.inactive { - color: if($is-dark-theme, #ffc1c1, #620e0e) !important; - background-color: if($is-dark-theme, map.get($background, state-inactive), #ffc1c1) !important; + color: if(sass($is-dark-theme): #ffc1c1; else: #620e0e) !important; + background-color: if(sass($is-dark-theme): map.get($background, state-inactive) ; else: #ffc1c1) !important; } } .color-shadow { - border: 1px solid if($is-dark-theme, rgba(#8795a1, 0.2), rgba(#8795a1, 0.2)); + border: 1px solid if(sass($is-dark-theme): rgba(#8795a1, 0.2) ; else: rgba(#8795a1, 0.2)); box-shadow: 0 0 3px #0000001a; } } diff --git a/console/src/tailwind.css b/console/src/tailwind.css new file mode 100644 index 000000000..bd14e97ad --- /dev/null +++ b/console/src/tailwind.css @@ -0,0 +1,5 @@ +/* https://github.com/tailwindlabs/tailwindcss-intellisense/issues/1158#issuecomment-2968949608 */ +@import 'tailwindcss/utilities' prefix(tw); +@import 'tailwindcss/theme' prefix(tw); + +@custom-variant dark (&:where(.dark-theme, .dark-theme *)); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f59c903fb..1eadfffe3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -432,14 +432,14 @@ importers: specifier: ^8.5.8 version: 8.5.8 prettier: - specifier: ^3.8.1 - version: 3.8.1 + specifier: ^3.8.3 + version: 3.8.3 prettier-plugin-organize-imports: specifier: ^4.3.0 - version: 4.3.0(prettier@3.8.1)(typescript@5.9.3) + version: 4.3.0(prettier@3.8.3)(typescript@5.9.3) prettier-plugin-tailwindcss: specifier: ^0.7.2 - version: 0.7.2(prettier-plugin-organize-imports@4.3.0(prettier@3.8.1)(typescript@5.9.3))(prettier@3.8.1) + version: 0.7.2(prettier-plugin-organize-imports@4.3.0(prettier@3.8.3)(typescript@5.9.3))(prettier@3.8.3) sass: specifier: ^1.98.0 version: 1.98.0 @@ -624,6 +624,9 @@ importers: '@eslint/js': specifier: ^10.0.1 version: 10.0.1(eslint@10.1.0(jiti@2.6.1)) + '@tailwindcss/postcss': + specifier: ^4.2.2 + version: 4.2.2 '@types/file-saver': specifier: ^2.0.7 version: 2.0.7 @@ -699,12 +702,18 @@ importers: karma-jasmine-html-reporter: specifier: ^2.2.0 version: 2.2.0(jasmine-core@5.12.1)(karma-jasmine@5.1.0(karma@6.4.4))(karma@6.4.4) + postcss: + specifier: ^8.5.8 + version: 8.5.8 prettier: - specifier: ^3.8.1 - version: 3.8.1 + specifier: ^3.8.3 + version: 3.8.3 prettier-plugin-organize-imports: specifier: ^4.3.0 - version: 4.3.0(prettier@3.8.1)(typescript@5.9.2) + version: 4.3.0(prettier@3.8.3)(typescript@5.9.2) + tailwindcss: + specifier: ^4.2.2 + version: 4.2.2 typescript: specifier: 5.9.2 version: 5.9.2 @@ -807,8 +816,8 @@ importers: specifier: ^9.0.3 version: 9.0.3 prettier: - specifier: ^3.8.1 - version: 3.8.1 + specifier: ^3.8.3 + version: 3.8.3 typescript: specifier: ^5.9.3 version: 5.9.3 @@ -10450,8 +10459,8 @@ packages: engines: {node: '>=10.13.0'} hasBin: true - prettier@3.8.1: - resolution: {integrity: sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==} + prettier@3.8.3: + resolution: {integrity: sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==} engines: {node: '>=14'} hasBin: true @@ -23386,25 +23395,25 @@ snapshots: prelude-ls@1.2.1: {} - prettier-plugin-organize-imports@4.3.0(prettier@3.8.1)(typescript@5.9.2): + prettier-plugin-organize-imports@4.3.0(prettier@3.8.3)(typescript@5.9.2): dependencies: - prettier: 3.8.1 + prettier: 3.8.3 typescript: 5.9.2 - prettier-plugin-organize-imports@4.3.0(prettier@3.8.1)(typescript@5.9.3): + prettier-plugin-organize-imports@4.3.0(prettier@3.8.3)(typescript@5.9.3): dependencies: - prettier: 3.8.1 + prettier: 3.8.3 typescript: 5.9.3 - prettier-plugin-tailwindcss@0.7.2(prettier-plugin-organize-imports@4.3.0(prettier@3.8.1)(typescript@5.9.3))(prettier@3.8.1): + prettier-plugin-tailwindcss@0.7.2(prettier-plugin-organize-imports@4.3.0(prettier@3.8.3)(typescript@5.9.3))(prettier@3.8.3): dependencies: - prettier: 3.8.1 + prettier: 3.8.3 optionalDependencies: - prettier-plugin-organize-imports: 4.3.0(prettier@3.8.1)(typescript@5.9.3) + prettier-plugin-organize-imports: 4.3.0(prettier@3.8.3)(typescript@5.9.3) prettier@2.8.8: {} - prettier@3.8.1: {} + prettier@3.8.3: {} pretty-bytes@5.6.0: {} diff --git a/tests/functional-ui/package.json b/tests/functional-ui/package.json index 9073920db..450b2f124 100644 --- a/tests/functional-ui/package.json +++ b/tests/functional-ui/package.json @@ -12,7 +12,7 @@ "cypress": "^15.13.0", "cypress-wait-until": "^3.0.2", "jsonwebtoken": "^9.0.3", - "prettier": "^3.8.1", + "prettier": "^3.8.3", "typescript": "^5.9.3", "uuid": "^13.0.0" }