diff --git a/console/package.json b/console/package.json
index f4b5ce7845..35cf161789 100644
--- a/console/package.json
+++ b/console/package.json
@@ -30,6 +30,8 @@
"@fortawesome/angular-fontawesome": "^0.13.0",
"@fortawesome/fontawesome-svg-core": "^6.7.2",
"@fortawesome/free-brands-svg-icons": "^6.7.2",
+ "@ng-icons/core": "^25.0.0",
+ "@ng-icons/heroicons": "^25.0.0",
"@ngx-translate/core": "^15.0.0",
"@tanstack/angular-query-experimental": "^5.75.4",
"@zitadel/client": "1.2.0",
diff --git a/console/src/app/app.component.ts b/console/src/app/app.component.ts
index 2441f6409d..78bd478345 100644
--- a/console/src/app/app.component.ts
+++ b/console/src/app/app.component.ts
@@ -240,7 +240,6 @@ export class AppComponent {
this.translate.onLangChange.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((language: LangChangeEvent) => {
this.document.documentElement.lang = language.lang;
- this.language = language.lang;
});
}
@@ -290,7 +289,6 @@ export class AppComponent {
? userprofile.human.profile?.preferredLanguage
: fallbackLang;
this.translate.use(lang);
- this.language = lang;
this.document.documentElement.lang = lang;
});
}
diff --git a/console/src/app/app.module.ts b/console/src/app/app.module.ts
index 08b52ea185..ae128bbcca 100644
--- a/console/src/app/app.module.ts
+++ b/console/src/app/app.module.ts
@@ -76,6 +76,7 @@ import { PosthogService } from './services/posthog.service';
import { NewHeaderComponent } from './modules/new-header/new-header.component';
import { provideTanStackQuery, QueryClient, withDevtools } from '@tanstack/angular-query-experimental';
import { CdkOverlayOrigin } from '@angular/cdk/overlay';
+import { provideNgIconsConfig } from '@ng-icons/core';
registerLocaleData(localeDe);
i18nIsoCountries.registerLocale(require('i18n-iso-countries/langs/de.json'));
@@ -251,6 +252,9 @@ const authConfig: AuthConfig = {
new QueryClient(),
withDevtools(() => ({ loadDevtools: 'auto' })),
),
+ provideNgIconsConfig({
+ size: '1rem',
+ }),
],
bootstrap: [AppComponent],
exports: [],
diff --git a/console/src/app/components/features/features.component.html b/console/src/app/components/features/features.component.html
index 30d8f629af..d7cfe64b88 100644
--- a/console/src/app/components/features/features.component.html
+++ b/console/src/app/components/features/features.component.html
@@ -24,9 +24,9 @@
*ngFor="let key of FEATURE_KEYS"
[toggleStateKey]="key"
[toggleState]="toggleStates[key]"
- (toggleChange)="saveFeatures(key, $event)"
+ (toggleChange)="saveFeatures(toggleStates, key, $event)"
>
-
+
diff --git a/console/src/app/components/features/features.component.ts b/console/src/app/components/features/features.component.ts
index d95bbdde43..4094172f79 100644
--- a/console/src/app/components/features/features.component.ts
+++ b/console/src/app/components/features/features.component.ts
@@ -21,7 +21,7 @@ import {
} from '@zitadel/proto/zitadel/feature/v2/instance_pb';
import { Source } from '@zitadel/proto/zitadel/feature/v2/feature_pb';
import { MessageInitShape } from '@bufbuild/protobuf';
-import { firstValueFrom, Observable, ReplaySubject, shareReplay, switchMap } from 'rxjs';
+import { Observable, ReplaySubject, shareReplay, switchMap } from 'rxjs';
import { filter, map, startWith } from 'rxjs/operators';
import { LoginV2FeatureToggleComponent } from '../feature-toggle/login-v2-feature-toggle/login-v2-feature-toggle.component';
@@ -133,18 +133,22 @@ export class FeaturesComponent {
);
}
- public async saveFeatures(key: TKey, value: TValue) {
- const toggleStates = { ...(await firstValueFrom(this.toggleStates$)), [key]: value };
+ public async saveFeatures(
+ toggleStates: ToggleStates,
+ key: TKey,
+ value: TValue,
+ ) {
+ const newToggleStates = { ...toggleStates, [key]: value };
const req = FEATURE_KEYS.reduce>((acc, key) => {
- acc[key] = toggleStates[key].enabled;
+ acc[key] = newToggleStates[key].enabled;
return acc;
}, {});
// to save special flags they have to be handled here
req.loginV2 = {
- required: toggleStates.loginV2.enabled,
- baseUri: toggleStates.loginV2.baseUri,
+ required: newToggleStates.loginV2.enabled,
+ baseUri: newToggleStates.loginV2.baseUri,
};
try {
diff --git a/console/src/app/modules/new-header/header-button/header-button.component.html b/console/src/app/modules/new-header/header-button/header-button.component.html
index 58b25bd806..33dc99ad73 100644
--- a/console/src/app/modules/new-header/header-button/header-button.component.html
+++ b/console/src/app/modules/new-header/header-button/header-button.component.html
@@ -1,6 +1,6 @@
diff --git a/console/src/app/modules/new-header/header-button/header-button.component.ts b/console/src/app/modules/new-header/header-button/header-button.component.ts
index 5d15b3f764..ae43c88132 100644
--- a/console/src/app/modules/new-header/header-button/header-button.component.ts
+++ b/console/src/app/modules/new-header/header-button/header-button.component.ts
@@ -1,4 +1,6 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
+import { NgIconComponent, provideIcons } from '@ng-icons/core';
+import { heroChevronUpDown } from '@ng-icons/heroicons/outline';
@Component({
selector: 'cnsl-header-button',
@@ -6,5 +8,7 @@ import { ChangeDetectionStrategy, Component } from '@angular/core';
styleUrls: ['./header-button.component.scss'],
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
+ imports: [NgIconComponent],
+ providers: [provideIcons({ heroChevronUpDown })],
})
export class HeaderButtonComponent {}
diff --git a/console/src/app/modules/new-header/instance-selector/instance-selector.component.html b/console/src/app/modules/new-header/instance-selector/instance-selector.component.html
index 115ddd44bf..b56f0f5296 100644
--- a/console/src/app/modules/new-header/instance-selector/instance-selector.component.html
+++ b/console/src/app/modules/new-header/instance-selector/instance-selector.component.html
@@ -2,13 +2,13 @@
{{ 'MENU.INSTANCEOVERVIEW' | translate }}
{{ instance.name }}
-
+
diff --git a/console/src/app/modules/new-header/instance-selector/instance-selector.component.ts b/console/src/app/modules/new-header/instance-selector/instance-selector.component.ts
index 818016a394..6c0e310a9a 100644
--- a/console/src/app/modules/new-header/instance-selector/instance-selector.component.ts
+++ b/console/src/app/modules/new-header/instance-selector/instance-selector.component.ts
@@ -3,6 +3,9 @@ import { TranslateModule } from '@ngx-translate/core';
import { MatButtonModule } from '@angular/material/button';
import { Router, RouterLink } from '@angular/router';
import { InstanceDetail } from '@zitadel/proto/zitadel/instance_pb';
+import { NgIconComponent, provideIcons } from '@ng-icons/core';
+import { heroCog8ToothSolid } from '@ng-icons/heroicons/solid';
+import { heroChevronRight } from '@ng-icons/heroicons/outline';
@Component({
selector: 'cnsl-instance-selector',
@@ -10,7 +13,8 @@ import { InstanceDetail } from '@zitadel/proto/zitadel/instance_pb';
styleUrls: ['./instance-selector.component.scss'],
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
- imports: [TranslateModule, MatButtonModule, RouterLink],
+ imports: [TranslateModule, MatButtonModule, RouterLink, NgIconComponent],
+ providers: [provideIcons({ heroCog8ToothSolid, heroChevronRight })],
})
export class InstanceSelectorComponent {
@Output() public instanceChanged = new EventEmitter();
diff --git a/console/src/app/modules/new-header/new-header.component.html b/console/src/app/modules/new-header/new-header.component.html
index 17c2b368c8..1d4ca50115 100644
--- a/console/src/app/modules/new-header/new-header.component.html
+++ b/console/src/app/modules/new-header/new-header.component.html
@@ -1,4 +1,5 @@
diff --git a/console/src/app/modules/new-header/new-header.component.scss b/console/src/app/modules/new-header/new-header.component.scss
index 615e1cc327..090e828c86 100644
--- a/console/src/app/modules/new-header/new-header.component.scss
+++ b/console/src/app/modules/new-header/new-header.component.scss
@@ -1,7 +1,14 @@
.new-header-wrapper {
+ padding-left: 5px;
padding-right: 5px;
display: flex;
flex-direction: row;
align-items: center;
gap: 5px;
}
+
+.new-header-title {
+ font-weight: 900;
+ letter-spacing: 3px;
+ font-size: 0.8rem;
+}
diff --git a/console/src/app/modules/new-header/new-header.component.ts b/console/src/app/modules/new-header/new-header.component.ts
index 0cc978f828..398bf77e37 100644
--- a/console/src/app/modules/new-header/new-header.component.ts
+++ b/console/src/app/modules/new-header/new-header.component.ts
@@ -17,6 +17,7 @@ import { toSignal } from '@angular/core/rxjs-interop';
import { BreakpointObserver } from '@angular/cdk/layout';
import { NewAdminService } from '../../services/new-admin.service';
import { NewAuthService } from '../../services/new-auth.service';
+import { RouterLink } from '@angular/router';
@Component({
selector: 'cnsl-new-header',
@@ -37,6 +38,7 @@ import { NewAuthService } from '../../services/new-auth.service';
NgTemplateOutlet,
AsyncPipe,
HasRolePipeModule,
+ RouterLink,
],
})
export class NewHeaderComponent {
diff --git a/console/src/app/modules/new-header/organization-selector/organization-selector.component.html b/console/src/app/modules/new-header/organization-selector/organization-selector.component.html
index e28a169265..50aeba32e4 100644
--- a/console/src/app/modules/new-header/organization-selector/organization-selector.component.html
+++ b/console/src/app/modules/new-header/organization-selector/organization-selector.component.html
@@ -1,17 +1,14 @@
-
-
-
diff --git a/console/src/app/modules/new-header/organization-selector/organization-selector.component.scss b/console/src/app/modules/new-header/organization-selector/organization-selector.component.scss
index 74d50a6911..0c43762045 100644
--- a/console/src/app/modules/new-header/organization-selector/organization-selector.component.scss
+++ b/console/src/app/modules/new-header/organization-selector/organization-selector.component.scss
@@ -62,7 +62,7 @@
.search-icon {
position: absolute;
top: 50%;
- transform: scaleX(-1) translate(0, -50%);
+ transform: translate(0, -50%);
// default input padding
left: 10px;
color: if($is-dark-theme, #ffffff60, #00000060);
diff --git a/console/src/app/modules/new-header/organization-selector/organization-selector.component.ts b/console/src/app/modules/new-header/organization-selector/organization-selector.component.ts
index d98b9dbb79..20ccf977b6 100644
--- a/console/src/app/modules/new-header/organization-selector/organization-selector.component.ts
+++ b/console/src/app/modules/new-header/organization-selector/organization-selector.component.ts
@@ -4,7 +4,7 @@ import { NewOrganizationService } from 'src/app/services/new-organization.servic
import { NgForOf, NgIf } from '@angular/common';
import { ToastService } from 'src/app/services/toast.service';
import { FormBuilder, FormControl, ReactiveFormsModule } from '@angular/forms';
-import { ListOrganizationsRequestSchema } from '@zitadel/proto/zitadel/org/v2/org_service_pb';
+import { ListOrganizationsRequestSchema, ListOrganizationsResponse } from '@zitadel/proto/zitadel/org/v2/org_service_pb';
import { MessageInitShape } from '@bufbuild/protobuf';
import { debounceTime } from 'rxjs/operators';
import { toSignal } from '@angular/core/rxjs-interop';
@@ -17,6 +17,9 @@ import { TranslateModule } from '@ngx-translate/core';
import { InputModule } from '../../input/input.module';
import { MatOptionModule } from '@angular/material/core';
import { Router } from '@angular/router';
+import { NgIconComponent, provideIcons } from '@ng-icons/core';
+import { heroCheck, heroMagnifyingGlass } from '@ng-icons/heroicons/outline';
+import { heroArrowLeftCircleSolid } from '@ng-icons/heroicons/solid';
type NameQuery = Extract<
NonNullable['queries']>[number]['query'],
@@ -41,7 +44,9 @@ const QUERY_LIMIT = 5;
MatMenuModule,
InputModule,
MatOptionModule,
+ NgIconComponent,
],
+ providers: [provideIcons({ heroCheck, heroMagnifyingGlass, heroArrowLeftCircleSolid })],
})
export class OrganizationSelectorComponent {
@Input()
@@ -142,29 +147,29 @@ export class OrganizationSelectorComponent {
queries: query ? [{ query }] : undefined,
},
placeholderData: keepPreviousData,
- getNextPageParam: (lastPage, _, pageParam) =>
- // if we received less than the limit last time we are at the end
- lastPage.result.length < pageParam.query.limit
- ? undefined
- : {
+ getNextPageParam: (lastPage, pages, pageParam) =>
+ this.countLoadedOrgs(pages) < (lastPage.details?.totalResult ?? BigInt(Number.MAX_SAFE_INTEGER))
+ ? {
...pageParam,
query: {
...pageParam.query,
offset: pageParam.query.offset + BigInt(lastPage.result.length),
},
- },
+ }
+ : undefined,
};
});
}
private getLoadedOrgsCount(organizationsQuery: ReturnType) {
- return computed(() => {
- const pages = organizationsQuery.data()?.pages;
- if (!pages) {
- return BigInt(0);
- }
- return pages.reduce((acc, page) => acc + BigInt(page.result.length), BigInt(0));
- });
+ return computed(() => this.countLoadedOrgs(organizationsQuery.data()?.pages));
+ }
+
+ private countLoadedOrgs(pages?: ListOrganizationsResponse[]) {
+ if (!pages) {
+ return BigInt(0);
+ }
+ return pages.reduce((acc, page) => acc + BigInt(page.result.length), BigInt(0));
}
private getActiveOrgIfSearchMatches(nameQuery: Signal) {
@@ -187,4 +192,6 @@ export class OrganizationSelectorComponent {
protected trackOrg(_: number, { id }: Organization): string {
return id;
}
+
+ protected readonly QUERY_LIMIT = QUERY_LIMIT;
}
diff --git a/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-detail.component.ts b/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-detail.component.ts
index aa75b72db3..096bc04089 100644
--- a/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-detail.component.ts
+++ b/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-detail.component.ts
@@ -89,14 +89,6 @@ export class AuthUserDetailComponent implements OnInit {
return '';
});
- protected savedLanguage = computed(() => {
- const user = this.user.data();
- if (!user || user.type.case !== 'human' || !user.type.value.profile?.preferredLanguage) {
- return this.translate.defaultLang;
- }
- return user.type.value.profile?.preferredLanguage;
- });
-
constructor(
private translate: TranslateService,
private toast: ToastService,
@@ -141,10 +133,6 @@ export class AuthUserDetailComponent implements OnInit {
this.toast.showError(error);
}
});
-
- effect(() => {
- this.translate.use(this.savedLanguage());
- });
}
ngOnInit(): void {
diff --git a/console/src/app/services/new-organization.service.ts b/console/src/app/services/new-organization.service.ts
index 3b02fa238e..db992e6805 100644
--- a/console/src/app/services/new-organization.service.ts
+++ b/console/src/app/services/new-organization.service.ts
@@ -44,8 +44,6 @@ export class NewOrganizationService {
}
public async setOrgId(orgId?: string) {
- console.log('beboop', orgId);
- console.trace(orgId);
const organization = await this.queryClient.fetchQuery(this.organizationByIdQueryOptions(orgId ?? this.getOrgId()()));
if (organization) {
this.storage.setItem(StorageKey.organizationId, orgId, StorageLocation.session);
diff --git a/console/yarn.lock b/console/yarn.lock
index 87b80f3aec..80b27f2b4a 100644
--- a/console/yarn.lock
+++ b/console/yarn.lock
@@ -2641,6 +2641,20 @@
read-package-up "^11.0.0"
semver "^7.3.8"
+"@ng-icons/core@^25.0.0":
+ version "25.6.1"
+ resolved "https://registry.yarnpkg.com/@ng-icons/core/-/core-25.6.1.tgz#471c2597af226c5b6f53ec5d39c8ec680964b9b2"
+ integrity sha512-o6vCttlzXvDZRYiOKOULr7fsX8gY/DwwxzBSrBQzwa/at+pC0xRoe6uczJ9Ato+y1EDWP/PlrEMAQfvokBA6tQ==
+ dependencies:
+ tslib "^2.2.0"
+
+"@ng-icons/heroicons@^25.0.0":
+ version "25.6.1"
+ resolved "https://registry.yarnpkg.com/@ng-icons/heroicons/-/heroicons-25.6.1.tgz#e6cd64c68c22e3ae4d93a1a1b7daa537f6d9d600"
+ integrity sha512-QGTIIl+S6/w2vQvYGP1zNLbNvJLLRS+1evlOPWZZzWow+77qRxs0E96CukSsjItBFUnLKvzuOfMBBcNtb2SIHQ==
+ dependencies:
+ tslib "^2.2.0"
+
"@ngtools/webpack@16.2.16":
version "16.2.16"
resolved "https://registry.yarnpkg.com/@ngtools/webpack/-/webpack-16.2.16.tgz#512da8f3459faafd0cc1f7f7cbec96b678377be6"
@@ -8719,7 +8733,7 @@ tslib@^2.0.0, tslib@^2.0.3, tslib@^2.3.0, tslib@^2.4.0, tslib@^2.4.1:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.7.0.tgz#d9b40c5c40ab59e8738f297df3087bf1a2690c01"
integrity sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==
-tslib@^2.1.0, tslib@^2.7.0:
+tslib@^2.1.0, tslib@^2.2.0, tslib@^2.7.0:
version "2.8.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f"
integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==