mirror of
https://github.com/andwati/zitadel.git
synced 2026-11-15 18:25:13 +00:00
feat(console): granted project detail (#203)
* granted project detail * support for grantedProjects * getGrantedProjectGrantByIDs * fix grant members bindings * seperate granted from owned project detail * lint * dont show apps on granted projects
This commit is contained in:
+49
@@ -0,0 +1,49 @@
|
||||
<app-meta-layout>
|
||||
<div class="max-width-container">
|
||||
<div class="head" *ngIf="project?.id">
|
||||
<a [routerLink]="[ '/projects' ]" mat-icon-button>
|
||||
<mat-icon class="icon">arrow_back</mat-icon>
|
||||
</a>
|
||||
<h1>{{ 'PROJECT.PAGES.TITLE' | translate }} {{project?.name}}</h1>
|
||||
|
||||
<div class="full-width">
|
||||
<p class="desc">{{ 'PROJECT.PAGES.DESCRIPTION' | translate }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- <ng-template appHasRole [appHasRole]="['project.app.read:' + project?.id, 'project.app.read']">
|
||||
<app-project-application-grid *ngIf="grid" [disabled]="project?.state !== ProjectState.PROJECTSTATE_ACTIVE"
|
||||
(changeView)="grid = false" [projectId]="projectId"></app-project-application-grid>
|
||||
<app-card *ngIf="!grid" title="{{ 'PROJECT.APP.TITLE' | translate }}">
|
||||
<card-actions class="card-actions">
|
||||
<button mat-icon-button (click)="grid = true">
|
||||
<i matTooltip="show grid view" class="las la-th-large"></i>
|
||||
</button>
|
||||
</card-actions>
|
||||
<app-project-applications [disabled]="project?.state !== ProjectState.PROJECTSTATE_ACTIVE"
|
||||
[projectId]="projectId"></app-project-applications>
|
||||
</app-card>
|
||||
</ng-template> -->
|
||||
|
||||
<app-card>
|
||||
<app-project-grant-members *ngIf="project && project.id && project.grantId"
|
||||
[disabled]="project?.state !== ProjectState.PROJECTSTATE_ACTIVE" [project]="project">
|
||||
</app-project-grant-members>
|
||||
</app-card>
|
||||
</div>
|
||||
<metainfo class="side">
|
||||
<div class="details">
|
||||
<div class="row">
|
||||
<span class="first">{{'PROJECT.TYPE.TITLE' | translate}}:</span>
|
||||
<span class="second">{{'PROJECT.TYPE.'+ ProjectType.PROJECTTYPE_GRANTED | translate}}</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<span class="first">{{'PROJECT.STATE.TITLE' | translate}}:</span>
|
||||
<span *ngIf="project && project.state !== undefined"
|
||||
class="second">{{'PROJECT.STATE.'+project.state | translate}}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<app-changes *ngIf="project" [changeType]="ChangeType.PROJECT" [id]="project.id"></app-changes>
|
||||
</metainfo>
|
||||
</app-meta-layout>
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
.head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid #ffffff20;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 1rem;
|
||||
|
||||
a {
|
||||
display: block;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 1.2rem;
|
||||
margin: 0 1rem;
|
||||
margin-left: 2rem;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.full-width {
|
||||
padding-top: 1rem;
|
||||
width: 100%;
|
||||
display: block;
|
||||
|
||||
.icon-button {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
button {
|
||||
border-radius: .5rem;
|
||||
}
|
||||
|
||||
.desc {
|
||||
font-size: .9rem;
|
||||
color: #81868a;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
margin: 0 -.5rem;
|
||||
|
||||
mat-form-field {
|
||||
flex: 1 1 33%;
|
||||
margin: 0 .5rem;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
.side {
|
||||
.details {
|
||||
margin-bottom: 1rem;
|
||||
border-bottom: 1px solid #81868a40;
|
||||
padding-bottom: 1rem;
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
margin-bottom: 0.5rem;
|
||||
align-items: center;
|
||||
|
||||
button {
|
||||
display: none;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
button {
|
||||
display: inline-block;
|
||||
visibility: visible;
|
||||
mat-icon {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.first {
|
||||
flex: 1;
|
||||
font-size: 0.8rem;
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
.fill-space {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.second {
|
||||
font-size: 0.8rem;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
a {
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.side-section {
|
||||
color: #81868a;
|
||||
}
|
||||
}
|
||||
|
||||
.mat-tab-label {
|
||||
min-width: 100px !important;
|
||||
}
|
||||
|
||||
.flex-col {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { GrantedProjectDetailComponent } from './granted-project-detail.component';
|
||||
|
||||
describe('GrantedProjectDetailComponent', () => {
|
||||
let component: GrantedProjectDetailComponent;
|
||||
let fixture: ComponentFixture<GrantedProjectDetailComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [GrantedProjectDetailComponent],
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(GrantedProjectDetailComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
import { SelectionModel } from '@angular/cdk/collections';
|
||||
import { Location } from '@angular/common';
|
||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { MatTableDataSource } from '@angular/material/table';
|
||||
import { ActivatedRoute, Params } from '@angular/router';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { from, Observable, of, Subscription } from 'rxjs';
|
||||
import { ChangeType } from 'src/app/modules/changes/changes.component';
|
||||
import {
|
||||
Application,
|
||||
ApplicationSearchResponse,
|
||||
GrantedProject,
|
||||
ProjectMember,
|
||||
ProjectMemberSearchResponse,
|
||||
ProjectRole,
|
||||
ProjectRoleSearchResponse,
|
||||
ProjectState,
|
||||
ProjectType,
|
||||
} from 'src/app/proto/generated/management_pb';
|
||||
import { GrpcService } from 'src/app/services/grpc.service';
|
||||
import { ProjectService } from 'src/app/services/project.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-granted-project-detail',
|
||||
templateUrl: './granted-project-detail.component.html',
|
||||
styleUrls: ['./granted-project-detail.component.scss'],
|
||||
})
|
||||
export class GrantedProjectDetailComponent implements OnInit, OnDestroy {
|
||||
public projectId: string = '';
|
||||
public grantId: string = '';
|
||||
public project!: GrantedProject.AsObject;
|
||||
|
||||
public pageSizeRoles: number = 10;
|
||||
public roleDataSource: MatTableDataSource<ProjectRole.AsObject> = new MatTableDataSource<ProjectRole.AsObject>();
|
||||
public roleResult!: ProjectRoleSearchResponse.AsObject;
|
||||
public roleColumns: string[] = ['name', 'displayname', 'group', 'actions'];
|
||||
|
||||
public pageSizeMembers: number = 10;
|
||||
public projectDataSource: MatTableDataSource<ProjectMember.AsObject> = new MatTableDataSource<ProjectMember.AsObject>();
|
||||
public memberResult!: ProjectMemberSearchResponse.AsObject;
|
||||
public memberColumns: string[] = ['firstname', 'lastname', 'username', 'email', 'roles'];
|
||||
public selection: SelectionModel<ProjectMember.AsObject> = new SelectionModel<ProjectMember.AsObject>(true, []);
|
||||
|
||||
public pageSizeApps: number = 10;
|
||||
public appsDataSource: MatTableDataSource<Application.AsObject> = new MatTableDataSource<Application.AsObject>();
|
||||
public appsResult!: ApplicationSearchResponse.AsObject;
|
||||
public appsColumns: string[] = ['name'];
|
||||
|
||||
public ProjectState: any = ProjectState;
|
||||
public ProjectType: any = ProjectType;
|
||||
public ChangeType: any = ChangeType;
|
||||
|
||||
public grid: boolean = true;
|
||||
private subscription?: Subscription;
|
||||
public editstate: boolean = false;
|
||||
|
||||
public isZitadel$: Observable<boolean> = of(false);
|
||||
private zitadelsub: Subscription = new Subscription();
|
||||
|
||||
constructor(
|
||||
public translate: TranslateService,
|
||||
private route: ActivatedRoute,
|
||||
private toast: ToastService,
|
||||
private projectService: ProjectService,
|
||||
private _location: Location,
|
||||
private grpcService: GrpcService,
|
||||
) {
|
||||
}
|
||||
|
||||
public ngOnInit(): void {
|
||||
this.subscription = this.route.params.subscribe(params => this.getData(params));
|
||||
}
|
||||
|
||||
public ngOnDestroy(): void {
|
||||
this.subscription?.unsubscribe();
|
||||
this.zitadelsub.unsubscribe();
|
||||
}
|
||||
|
||||
private async getData({ id, grantId }: Params): Promise<void> {
|
||||
this.projectId = id;
|
||||
this.grantId = grantId;
|
||||
|
||||
if (this.projectId && this.grantId) {
|
||||
this.projectService.GetGrantedProjectGrantByID(this.projectId, this.grantId).then(proj => {
|
||||
this.project = proj.toObject();
|
||||
console.log(this.project);
|
||||
this.isZitadel$ = from(this.projectService.SearchApplications(this.project.id, 100, 0).then(appsResp => {
|
||||
const ret = appsResp.toObject().resultList
|
||||
.filter(app => app.oidcConfig?.clientId === this.grpcService.clientid).length > 0;
|
||||
return ret;
|
||||
}));
|
||||
}).catch(error => {
|
||||
this.toast.showError(error.message);
|
||||
});
|
||||
}
|
||||
|
||||
this.zitadelsub = this.isZitadel$.subscribe(isZita => console.log(`zitade: ${isZita}`));
|
||||
}
|
||||
|
||||
public navigateBack(): void {
|
||||
this._location.back();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user