mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-11 20:37:30 +00:00
fixes from testing
This commit is contained in:
@@ -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",
|
||||
|
@@ -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;
|
||||
});
|
||||
}
|
||||
|
@@ -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: [],
|
||||
|
@@ -24,9 +24,9 @@
|
||||
*ngFor="let key of FEATURE_KEYS"
|
||||
[toggleStateKey]="key"
|
||||
[toggleState]="toggleStates[key]"
|
||||
(toggleChange)="saveFeatures(key, $event)"
|
||||
(toggleChange)="saveFeatures(toggleStates, key, $event)"
|
||||
></cnsl-feature-toggle>
|
||||
<cnsl-login-v2-feature-toggle [toggleState]="toggleStates.loginV2" (toggleChanged)="saveFeatures('loginV2', $event)" />
|
||||
<cnsl-login-v2-feature-toggle [toggleState]="toggleStates.loginV2" (toggleChanged)="saveFeatures(toggleStates, 'loginV2', $event)" />
|
||||
</div>
|
||||
</cnsl-card>
|
||||
</div>
|
||||
|
@@ -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<TKey extends ToggleStateKeys, TValue extends ToggleStates[TKey]>(key: TKey, value: TValue) {
|
||||
const toggleStates = { ...(await firstValueFrom(this.toggleStates$)), [key]: value };
|
||||
public async saveFeatures<TKey extends ToggleStateKeys, TValue extends ToggleStates[TKey]>(
|
||||
toggleStates: ToggleStates,
|
||||
key: TKey,
|
||||
value: TValue,
|
||||
) {
|
||||
const newToggleStates = { ...toggleStates, [key]: value };
|
||||
|
||||
const req = FEATURE_KEYS.reduce<MessageInitShape<typeof SetInstanceFeaturesRequestSchema>>((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 {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<button class="header-button" cnslInput>
|
||||
<ng-content></ng-content>
|
||||
<div class="cnsl-action-button">
|
||||
<i class="las la-arrows-alt-v"></i>
|
||||
<ng-icon size="1.2rem" name="heroChevronUpDown"></ng-icon>
|
||||
</div>
|
||||
</button>
|
||||
|
@@ -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 {}
|
||||
|
@@ -2,13 +2,13 @@
|
||||
<span class="dropdown-label">{{ 'MENU.INSTANCEOVERVIEW' | translate }}</span>
|
||||
<a (click)="setInstance(instance)" mat-button class="dropdown-button"
|
||||
>{{ instance.name }}
|
||||
<i class="las la-1x la-angle-right"></i>
|
||||
<ng-icon name="heroChevronRight"></ng-icon>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
<a [routerLink]="['/instance']" (click)="settingsClicked.emit()" mat-button class="dropdown-button settings-button">
|
||||
<h3>{{ 'MENU.SETTINGS' | translate }}</h3>
|
||||
<i class="las la-1x la-cog"></i>
|
||||
<ng-icon name="heroCog8ToothSolid"></ng-icon>
|
||||
</a>
|
||||
</div>
|
||||
|
@@ -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<string>();
|
||||
|
@@ -1,4 +1,5 @@
|
||||
<div class="new-header-wrapper">
|
||||
<span routerLink="/" class="new-header-title">CONSOLE</span>
|
||||
<ng-container *ngIf="myInstanceQuery.data()?.instance as instance">
|
||||
<ng-container *ngTemplateOutlet="slash"></ng-container>
|
||||
<cnsl-header-button
|
||||
@@ -26,6 +27,7 @@
|
||||
></cnsl-organization-selector>
|
||||
</cnsl-header-dropdown>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="['org.read'] | hasRole | async">
|
||||
<ng-container *ngTemplateOutlet="slash"></ng-container>
|
||||
<cnsl-header-button cdkOverlayOrigin #orgTrigger="cdkOverlayOrigin" (click)="isOrgDropdownOpen.set(!isOrgDropdownOpen())">
|
||||
<ng-container *ngIf="activeOrganizationQuery.data() as org">{{ org.name }}</ng-container>
|
||||
@@ -33,6 +35,7 @@
|
||||
<cnsl-header-dropdown [trigger]="orgTrigger" [isOpen]="isOrgDropdownOpen()" (closed)="isOrgDropdownOpen.set(false)">
|
||||
<cnsl-organization-selector (orgChanged)="isOrgDropdownOpen.set(false)"></cnsl-organization-selector>
|
||||
</cnsl-header-dropdown>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
<ng-template #slash>
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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 {
|
||||
|
@@ -1,17 +1,14 @@
|
||||
<div cdkTrapFocus class="focus-trapper">
|
||||
<!-- <div *ngIf="organizationsQuery.isPending() || setOrgId.isPending()">-->
|
||||
<!-- Loading organizations...-->
|
||||
<!-- </div>-->
|
||||
<div class="org-header">
|
||||
<button *ngIf="backButton" (click)="backButtonPressed.emit()" mat-button class="dropdown-button">
|
||||
<span class="back-button">
|
||||
<i class="las la-arrow-alt-circle-left"></i>
|
||||
<ng-icon name="heroArrowLeftCircleSolid"></ng-icon>
|
||||
<h3>Back to {{ backButton }}</h3>
|
||||
</span>
|
||||
</button>
|
||||
<span class="dropdown-label">{{ 'MENU.ORGANIZATION' | translate }}</span>
|
||||
<form [formGroup]="form" class="form">
|
||||
<i class="las la-1x la-search search-icon"></i>
|
||||
<ng-icon class="search-icon" name="heroMagnifyingGlass"></ng-icon>
|
||||
<input
|
||||
class="search-input"
|
||||
autocomplete="off"
|
||||
@@ -25,7 +22,7 @@
|
||||
<!-- Make sure active org is always at the top -->
|
||||
<a *ngIf="activeOrgIfSearchMatches() as org" class="dropdown-button" mat-button (click)="changeOrg(org.id)">
|
||||
{{ org.name }}
|
||||
<i class="las la-1x la-check"></i>
|
||||
<ng-icon name="heroCheck"></ng-icon>
|
||||
</a>
|
||||
<ng-container *ngFor="let page of organizationsQuery.data()?.pages; last as lastPage">
|
||||
<ng-container *ngFor="let org of page.result; trackBy: trackOrg">
|
||||
@@ -33,15 +30,17 @@
|
||||
{{ org.name }}
|
||||
</a>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="lastPage && page.details?.totalResult as totalResult">
|
||||
<button
|
||||
class="dropdown-button"
|
||||
mat-stroked-button
|
||||
*ngIf="lastPage && page.details?.totalResult as totalResult"
|
||||
*ngIf="totalResult > QUERY_LIMIT"
|
||||
(click)="organizationsQuery.fetchNextPage()"
|
||||
[disabled]="!organizationsQuery.hasNextPage() || organizationsQuery.isFetchingNextPage()"
|
||||
>
|
||||
...{{ totalResult - loadedOrgsCount() }} {{ 'PAGINATOR.MORE' | translate }}
|
||||
</button>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -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);
|
||||
|
@@ -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<MessageInitShape<typeof ListOrganizationsRequestSchema>['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<typeof this.getOrganizationsQuery>) {
|
||||
return computed(() => {
|
||||
const pages = organizationsQuery.data()?.pages;
|
||||
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<NameQuery | undefined>) {
|
||||
@@ -187,4 +192,6 @@ export class OrganizationSelectorComponent {
|
||||
protected trackOrg(_: number, { id }: Organization): string {
|
||||
return id;
|
||||
}
|
||||
|
||||
protected readonly QUERY_LIMIT = QUERY_LIMIT;
|
||||
}
|
||||
|
@@ -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 {
|
||||
|
@@ -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);
|
||||
|
@@ -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==
|
||||
|
Reference in New Issue
Block a user