mirror of
https://github.com/andwati/zitadel.git
synced 2026-10-30 02:52:16 +00:00
fix: new context component (#2823)
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
<div class="card" cnslOutsideClick (clickOutside)="closeCard($event)">
|
||||
<div class="spinner-w">
|
||||
<mat-spinner diameter="20" *ngIf="orgLoading$ | async" color="accent">
|
||||
</mat-spinner>
|
||||
</div>
|
||||
|
||||
<div class="filter-wrapper">
|
||||
<input cnslInput class="filter-input" [formControl]="filterControl" autocomplete="off"
|
||||
(click)="$event.stopPropagation()" placeholder="{{'ORG.PAGES.FILTERPLACEHOLDER' | translate}}" #input>
|
||||
</div>
|
||||
|
||||
<div class="org-wrapper">
|
||||
<button mat-button [ngClass]="{'active': temporg.id === org?.id}" [disabled]="!temporg.id"
|
||||
*ngFor="let temporg of orgs$ | async" (click)="setActiveOrg(temporg)">
|
||||
{{temporg?.name ? temporg.name : 'NO NAME'}}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<button mat-button class="show-all" [routerLink]="[ '/org/overview' ]">{{'MENU.SHOWORGS' |
|
||||
translate}}</button>
|
||||
|
||||
<ng-template cnslHasRole [hasRole]="['org.create','iam.write']">
|
||||
<button mat-button [routerLink]="[ '/org/create' ]">
|
||||
<mat-icon class="avatar">add</mat-icon>
|
||||
{{'MENU.NEWORG' | translate}}
|
||||
</button>
|
||||
</ng-template>
|
||||
</div>
|
||||
@@ -0,0 +1,43 @@
|
||||
.card {
|
||||
border-radius: 0.5rem;
|
||||
z-index: 200;
|
||||
border: 1px solid #ffffff30;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 0;
|
||||
min-width: 220px;
|
||||
padding-bottom: 0.5rem;
|
||||
position: relative;
|
||||
|
||||
.filter-wrapper {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.spinner-w {
|
||||
top: 1rem;
|
||||
left: 0;
|
||||
right: 0;
|
||||
position: absolute;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.show-all {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.org-wrapper {
|
||||
max-height: 350px;
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
flex-direction: column;
|
||||
overflow-y: auto;
|
||||
width: 100%;
|
||||
|
||||
button {
|
||||
text-align: start;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
|
||||
import { OrgContextComponent } from './org-context.component';
|
||||
|
||||
describe('OrgContextComponent', () => {
|
||||
let component: OrgContextComponent;
|
||||
let fixture: ComponentFixture<OrgContextComponent>;
|
||||
|
||||
beforeEach(
|
||||
waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [OrgContextComponent],
|
||||
}).compileComponents();
|
||||
}),
|
||||
);
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(OrgContextComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,79 @@
|
||||
import { Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
|
||||
import { FormControl } from '@angular/forms';
|
||||
import { BehaviorSubject, catchError, debounceTime, finalize, from, map, Observable, of } from 'rxjs';
|
||||
import { TextQueryMethod } from 'src/app/proto/generated/zitadel/object_pb';
|
||||
import { Org, OrgNameQuery, OrgQuery } 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';
|
||||
|
||||
@Component({
|
||||
selector: 'cnsl-org-context',
|
||||
templateUrl: './org-context.component.html',
|
||||
styleUrls: ['./org-context.component.scss'],
|
||||
})
|
||||
export class OrgContextComponent implements OnInit {
|
||||
public orgLoading$: BehaviorSubject<any> = new BehaviorSubject(false);
|
||||
public orgs$: Observable<Org.AsObject[]> = of([]);
|
||||
public filterControl: FormControl = new FormControl('');
|
||||
@Input() public org!: Org.AsObject;
|
||||
@ViewChild('input', { static: false }) input!: ElementRef;
|
||||
@Output() public closedCard: EventEmitter<void> = new EventEmitter();
|
||||
@Output() public setOrg: EventEmitter<Org.AsObject> = new EventEmitter();
|
||||
|
||||
constructor(public authService: AuthenticationService, private auth: GrpcAuthService) {
|
||||
this.filterControl.valueChanges.pipe(debounceTime(500)).subscribe((value) => {
|
||||
this.loadOrgs(value.trim().toLowerCase());
|
||||
});
|
||||
}
|
||||
|
||||
public ngOnInit(): void {
|
||||
this.focusFilter();
|
||||
this.loadOrgs();
|
||||
}
|
||||
|
||||
public setActiveOrg(org: Org.AsObject) {
|
||||
this.setOrg.emit(org);
|
||||
this.closedCard.emit();
|
||||
}
|
||||
|
||||
public loadOrgs(filter?: string): void {
|
||||
if (!filter) {
|
||||
const value = this.input?.nativeElement?.value;
|
||||
if (value) {
|
||||
filter = value;
|
||||
}
|
||||
}
|
||||
|
||||
let query;
|
||||
if (filter) {
|
||||
query = new OrgQuery();
|
||||
const orgNameQuery = new OrgNameQuery();
|
||||
orgNameQuery.setName(filter);
|
||||
orgNameQuery.setMethod(TextQueryMethod.TEXT_QUERY_METHOD_CONTAINS_IGNORE_CASE);
|
||||
query.setNameQuery(orgNameQuery);
|
||||
}
|
||||
|
||||
this.orgLoading$.next(true);
|
||||
this.orgs$ = from(this.auth.listMyProjectOrgs(10, 0, query ? [query] : undefined)).pipe(
|
||||
map((resp) => {
|
||||
return resp.resultList.sort((left, right) => left.name.localeCompare(right.name));
|
||||
}),
|
||||
catchError(() => of([])),
|
||||
finalize(() => {
|
||||
this.orgLoading$.next(false);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
public closeCard(element: HTMLElement): void {
|
||||
if (!element.classList.contains('dontcloseonclick') && !element.classList.contains('mat-button-wrapper')) {
|
||||
this.closedCard.emit();
|
||||
}
|
||||
}
|
||||
|
||||
private focusFilter(): void {
|
||||
setTimeout(() => {
|
||||
this.input.nativeElement.focus();
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
|
||||
import { OutsideClickModule } from 'src/app/directives/outside-click/outside-click.module';
|
||||
|
||||
import { InputModule } from '../input/input.module';
|
||||
import { OrgContextComponent } from './org-context.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [OrgContextComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
MatIconModule,
|
||||
RouterModule,
|
||||
MatProgressSpinnerModule,
|
||||
MatButtonModule,
|
||||
InputModule,
|
||||
OutsideClickModule,
|
||||
TranslateModule,
|
||||
MatButtonModule,
|
||||
HasRoleModule,
|
||||
],
|
||||
exports: [OrgContextComponent],
|
||||
})
|
||||
export class OrgContextModule {}
|
||||
Reference in New Issue
Block a user