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:
Max Peintner
2020-06-10 15:52:56 +02:00
committed by GitHub
parent 9965beee9c
commit a35afd2b88
23 changed files with 2404 additions and 1773 deletions
@@ -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>
@@ -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;
}
}
@@ -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();
});
});
@@ -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();
}
}