mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-12 08:37:32 +00:00
fix(console): cleanup contributor module, move loading state to shared module, button visibility in light theme (#504)
* refreshtable component * project grant refresh table * project role refresh, user grant, i18n * lint * auth user mfa table * auth mfa table * rm unused 404 page, add mgmt mfa table * change light accent color * add actions to mfa table * user detail mfa table * clear selection on refresh, bind data length * member table * fix padding mfa table * Update console/src/assets/i18n/en.json Co-authored-by: Florian Forster <florian@caos.ch> * Update console/src/assets/i18n/en.json Co-authored-by: Florian Forster <florian@caos.ch> * z-index, new colors * new senf color * create stepper * app create stepper * i18n * i18n sections, header titles * lint * add pro mode * main contributor component * drop project members shared module * project detail members * org contributors, iam contributors * invert card and background colors in light design * changes card design * lighten meta background * account card radius * fix imports, global user email link * move spinner to refresh-table component * Update console/src/assets/i18n/de.json Co-authored-by: Florian Forster <florian@caos.ch> * Update console/src/assets/i18n/de.json Co-authored-by: Florian Forster <florian@caos.ch> * Update console/src/assets/i18n/de.json Co-authored-by: Florian Forster <florian@caos.ch> * Update console/src/assets/i18n/en.json Co-authored-by: Florian Forster <florian@caos.ch> * Update console/src/assets/i18n/de.json Co-authored-by: Florian Forster <florian@caos.ch> * Update console/src/assets/i18n/de.json Co-authored-by: Florian Forster <florian@caos.ch> * Update console/src/assets/i18n/en.json Co-authored-by: Florian Forster <florian@caos.ch> * Update console/src/assets/i18n/en.json Co-authored-by: Florian Forster <florian@caos.ch> * Update console/src/assets/i18n/en.json Co-authored-by: Florian Forster <florian@caos.ch> * light background on light design * Update console/src/assets/i18n/de.json Co-authored-by: Florian Forster <florian@caos.ch> * Update console/src/assets/i18n/de.json Co-authored-by: Florian Forster <florian@caos.ch> Co-authored-by: Florian Forster <florian@caos.ch>
This commit is contained in:
@@ -58,7 +58,8 @@
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
top: 60px;
|
top: 60px;
|
||||||
right: 0;
|
right: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
border-radius: .5rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -202,7 +203,7 @@
|
|||||||
span {
|
span {
|
||||||
border: 1px solid #ffffff10;
|
border: 1px solid #ffffff10;
|
||||||
padding: 2px 1rem;
|
padding: 2px 1rem;
|
||||||
border-radius: .5rem;
|
border-radius: 50vw;
|
||||||
color: #8795a1;
|
color: #8795a1;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
@@ -29,7 +29,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
button {
|
button {
|
||||||
border-radius: 1rem;
|
border-radius: 50vh;
|
||||||
margin: .5rem;
|
margin: .5rem;
|
||||||
|
|
||||||
.mat-button-wrapper {
|
.mat-button-wrapper {
|
||||||
|
@@ -1,11 +1,11 @@
|
|||||||
<div class="groups">
|
<div class="groups">
|
||||||
<span class="header">{{ 'PROJECT.MEMBER.TITLE' | translate }}</span>
|
<span class="header">{{ title }}</span>
|
||||||
<span class="sub-header">{{ 'PROJECT.MEMBER.TITLEDESC' | translate }}</span>
|
<span class="sub-header">{{ description }}</span>
|
||||||
<div class="people">
|
<div class="people">
|
||||||
<div class="img-list">
|
<div class="img-list">
|
||||||
<ng-container *ngIf="totalResult < 10; else compact">
|
<ng-container *ngIf="totalResult < 10; else compact">
|
||||||
<ng-container *ngFor="let member of membersSubject | async; index as i">
|
<ng-container *ngFor="let member of membersSubject | async; index as i">
|
||||||
<div (click)="showDetail()" class="avatar-circle"
|
<div (click)="emitShowDetail()" class="avatar-circle"
|
||||||
matTooltip="{{ member.email }} | {{member.rolesList?.join(' ')}}"
|
matTooltip="{{ member.email }} | {{member.rolesList?.join(' ')}}"
|
||||||
[ngStyle]="{'z-index': 100 - i}">
|
[ngStyle]="{'z-index': 100 - i}">
|
||||||
<app-avatar *ngIf="member && (member.displayName || (member.firstName && member.lastName))"
|
<app-avatar *ngIf="member && (member.displayName || (member.firstName && member.lastName))"
|
||||||
@@ -17,12 +17,12 @@
|
|||||||
</ng-container>
|
</ng-container>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-template #compact>
|
<ng-template #compact>
|
||||||
<div (click)="showDetail()" class="avatar-circle" matTooltip="Click to show detail">
|
<div (click)="emitShowDetail()" class="avatar-circle" matTooltip="Click to show detail">
|
||||||
<span>{{totalResult}}</span>
|
<span>{{totalResult}}</span>
|
||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
<button class="add-img" (click)="openAddMember()" [disabled]="org?.state !== OrgState.ORGSTATE_ACTIVE"
|
<button class="add-img" (click)="emitAddMember()" [disabled]="disabled" mat-icon-button
|
||||||
mat-icon-button aria-label="Edit contributors">
|
aria-label="Edit contributors">
|
||||||
<mat-icon>add</mat-icon>
|
<mat-icon>add</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
@@ -1,20 +1,20 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { ProjectContributorsComponent } from './project-contributors.component';
|
import { ContributorsComponent } from './contributors.component';
|
||||||
|
|
||||||
describe('ProjectContributorsComponent', () => {
|
describe('ContributorsComponent', () => {
|
||||||
let component: ProjectContributorsComponent;
|
let component: ContributorsComponent;
|
||||||
let fixture: ComponentFixture<ProjectContributorsComponent>;
|
let fixture: ComponentFixture<ContributorsComponent>;
|
||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
declarations: [ProjectContributorsComponent],
|
declarations: [ContributorsComponent],
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(ProjectContributorsComponent);
|
fixture = TestBed.createComponent(ContributorsComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
@@ -0,0 +1,25 @@
|
|||||||
|
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||||
|
import { BehaviorSubject } from 'rxjs';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-contributors',
|
||||||
|
templateUrl: './contributors.component.html',
|
||||||
|
styleUrls: ['./contributors.component.scss'],
|
||||||
|
})
|
||||||
|
export class ContributorsComponent {
|
||||||
|
@Input() title: string = '';
|
||||||
|
@Input() description: string = '';
|
||||||
|
@Input() disabled: boolean = false;
|
||||||
|
@Input() totalResult: number = 0;
|
||||||
|
@Input() membersSubject!: BehaviorSubject<any[]>;
|
||||||
|
@Output() addClicked: EventEmitter<void> = new EventEmitter();
|
||||||
|
@Output() showDetailClicked: EventEmitter<void> = new EventEmitter();
|
||||||
|
|
||||||
|
public emitAddMember(): void {
|
||||||
|
this.addClicked.emit();
|
||||||
|
}
|
||||||
|
|
||||||
|
public emitShowDetail(): void {
|
||||||
|
this.showDetailClicked.emit();
|
||||||
|
}
|
||||||
|
}
|
@@ -3,28 +3,23 @@ import { NgModule } from '@angular/core';
|
|||||||
import { MatButtonModule } from '@angular/material/button';
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
import { MatIconModule } from '@angular/material/icon';
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
|
||||||
|
|
||||||
import { MemberCreateDialogModule } from '../add-member-dialog/member-create-dialog.module';
|
|
||||||
import { AvatarModule } from '../avatar/avatar.module';
|
import { AvatarModule } from '../avatar/avatar.module';
|
||||||
import { ProjectContributorsComponent } from './project-contributors.component';
|
import { ContributorsComponent } from './contributors.component';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [ProjectContributorsComponent],
|
declarations: [ContributorsComponent],
|
||||||
imports: [
|
imports: [
|
||||||
MemberCreateDialogModule,
|
|
||||||
CommonModule,
|
CommonModule,
|
||||||
TranslateModule,
|
|
||||||
MatTooltipModule,
|
|
||||||
MatIconModule,
|
|
||||||
MatButtonModule,
|
|
||||||
AvatarModule,
|
AvatarModule,
|
||||||
|
MatIconModule,
|
||||||
|
MatTooltipModule,
|
||||||
|
MatButtonModule,
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
ProjectContributorsComponent,
|
ContributorsComponent,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class ProjectContributorsModule { }
|
export class ContributorsModule { }
|
||||||
|
|
@@ -1,32 +0,0 @@
|
|||||||
<div class="groups">
|
|
||||||
<span class="header">{{ 'PROJECT.MEMBER.TITLE' | translate }}</span>
|
|
||||||
<span class="sub-header">{{ 'PROJECT.MEMBER.TITLEDESC' | translate }}</span>
|
|
||||||
<div class="people">
|
|
||||||
<div class="img-list">
|
|
||||||
<ng-container *ngIf="totalResult < 10; else compact">
|
|
||||||
<ng-container *ngFor="let member of membersSubject | async; index as i">
|
|
||||||
<div (click)="showDetail()" class="avatar-circle" [ngStyle]="{'z-index': 100 - i}"
|
|
||||||
matTooltip="{{ member.email }} | {{member.rolesList?.join(' ')}}">
|
|
||||||
<app-avatar *ngIf="member && member.firstName && member.lastName; else thumbavatar"
|
|
||||||
class="avatar dontcloseonclick" [name]="member.firstName + ' '+ member.lastName"
|
|
||||||
[size]="32">
|
|
||||||
</app-avatar>
|
|
||||||
<ng-template #thumbavatar>
|
|
||||||
<i class="avatar las la-tools"></i>
|
|
||||||
</ng-template>
|
|
||||||
</div>
|
|
||||||
</ng-container>
|
|
||||||
</ng-container>
|
|
||||||
<ng-template #compact>
|
|
||||||
<div (click)="showDetail()" class="avatar-circle" matTooltip="Click to show detail">
|
|
||||||
<span>{{totalResult}}</span>
|
|
||||||
</div>
|
|
||||||
</ng-template>
|
|
||||||
<button class="add-img" (click)="openAddMember()"
|
|
||||||
[disabled]="project?.state !== ProjectState.PROJECTSTATE_ACTIVE" mat-icon-button
|
|
||||||
aria-label="Edit contributors">
|
|
||||||
<mat-icon>add</mat-icon>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
@@ -1,119 +0,0 @@
|
|||||||
import { Component, Input, OnInit } from '@angular/core';
|
|
||||||
import { MatDialog } from '@angular/material/dialog';
|
|
||||||
import { Router } from '@angular/router';
|
|
||||||
import { BehaviorSubject, from, of } from 'rxjs';
|
|
||||||
import { catchError, finalize, map } from 'rxjs/operators';
|
|
||||||
import {
|
|
||||||
ProjectGrantView,
|
|
||||||
ProjectMemberSearchResponse,
|
|
||||||
ProjectMemberView,
|
|
||||||
ProjectState,
|
|
||||||
ProjectType,
|
|
||||||
ProjectView,
|
|
||||||
User,
|
|
||||||
} from 'src/app/proto/generated/management_pb';
|
|
||||||
import { ProjectService } from 'src/app/services/project.service';
|
|
||||||
import { ToastService } from 'src/app/services/toast.service';
|
|
||||||
|
|
||||||
import { CreationType, MemberCreateDialogComponent } from '../../modules/add-member-dialog/member-create-dialog.component';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-project-contributors',
|
|
||||||
templateUrl: './project-contributors.component.html',
|
|
||||||
styleUrls: ['./project-contributors.component.scss'],
|
|
||||||
})
|
|
||||||
export class ProjectContributorsComponent implements OnInit {
|
|
||||||
@Input() public project!: ProjectView.AsObject | ProjectGrantView.AsObject;
|
|
||||||
@Input() public grantId: string = '';
|
|
||||||
@Input() public projectType!: ProjectType;
|
|
||||||
|
|
||||||
@Input() public disabled: boolean = false;
|
|
||||||
|
|
||||||
public totalResult: number = 0;
|
|
||||||
public membersSubject: BehaviorSubject<ProjectMemberView.AsObject[]>
|
|
||||||
= new BehaviorSubject<ProjectMemberView.AsObject[]>([]);
|
|
||||||
public ProjectState: any = ProjectState;
|
|
||||||
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
|
||||||
|
|
||||||
public ProjectType: any = ProjectType;
|
|
||||||
|
|
||||||
constructor(private projectService: ProjectService,
|
|
||||||
private dialog: MatDialog,
|
|
||||||
private toast: ToastService,
|
|
||||||
private router: Router) { }
|
|
||||||
|
|
||||||
public ngOnInit(): void {
|
|
||||||
const promise: Promise<ProjectMemberSearchResponse> | undefined =
|
|
||||||
this.projectType === ProjectType.PROJECTTYPE_OWNED ?
|
|
||||||
this.projectService.SearchProjectMembers(this.project.projectId, 100, 0) :
|
|
||||||
this.projectType === ProjectType.PROJECTTYPE_GRANTED ?
|
|
||||||
this.projectService.SearchProjectGrantMembers(this.project.projectId,
|
|
||||||
this.project.projectId, 100, 0) : undefined;
|
|
||||||
if (promise) {
|
|
||||||
from(promise).pipe(
|
|
||||||
map(resp => {
|
|
||||||
this.totalResult = resp.toObject().totalResult;
|
|
||||||
return resp.toObject().resultList;
|
|
||||||
}),
|
|
||||||
catchError(() => of([])),
|
|
||||||
finalize(() => this.loadingSubject.next(false)),
|
|
||||||
).subscribe(members => {
|
|
||||||
this.membersSubject.next(members);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public openAddMember(): void {
|
|
||||||
const dialogRef = this.dialog.open(MemberCreateDialogComponent, {
|
|
||||||
data: {
|
|
||||||
// TODO replace
|
|
||||||
creationType: this.projectType === ProjectType.PROJECTTYPE_OWNED ? CreationType.PROJECT_OWNED :
|
|
||||||
ProjectType.PROJECTTYPE_GRANTED ? CreationType.PROJECT_GRANTED : ProjectType.PROJECTTYPE_OWNED,
|
|
||||||
projectId: this.project.projectId,
|
|
||||||
},
|
|
||||||
width: '400px',
|
|
||||||
});
|
|
||||||
|
|
||||||
dialogRef.afterClosed().subscribe(resp => {
|
|
||||||
if (resp) {
|
|
||||||
const users: User.AsObject[] = resp.users;
|
|
||||||
const roles: string[] = resp.roles;
|
|
||||||
|
|
||||||
if (users && users.length && roles && roles.length) {
|
|
||||||
users.forEach(user => {
|
|
||||||
switch (this.projectType) {
|
|
||||||
case ProjectType.PROJECTTYPE_OWNED:
|
|
||||||
return this.projectService.AddProjectMember(this.project.projectId, user.id, roles)
|
|
||||||
.then(() => {
|
|
||||||
this.toast.showInfo('PROJECT.TOAST.MEMBERADDED', true);
|
|
||||||
}).catch(error => {
|
|
||||||
this.toast.showError(error);
|
|
||||||
});
|
|
||||||
case ProjectType.PROJECTTYPE_GRANTED:
|
|
||||||
return this.projectService.AddProjectGrantMember(
|
|
||||||
this.project.projectId,
|
|
||||||
this.grantId,
|
|
||||||
user.id,
|
|
||||||
roles,
|
|
||||||
).then(() => {
|
|
||||||
this.toast.showInfo('PROJECT.TOAST.MEMBERADDED', true);
|
|
||||||
}).catch(error => {
|
|
||||||
this.toast.showError(error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public showDetail(): void {
|
|
||||||
if (this.project?.state === ProjectState.PROJECTSTATE_ACTIVE) {
|
|
||||||
if (this.projectType === ProjectType.PROJECTTYPE_GRANTED) {
|
|
||||||
this.router.navigate(['granted-projects', this.project.projectId, 'grant', this.grantId, 'members']);
|
|
||||||
} else if (this.projectType === ProjectType.PROJECTTYPE_OWNED) {
|
|
||||||
this.router.navigate(['projects', this.project.projectId, 'members']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -11,7 +11,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<app-refresh-table *ngIf="project" (refreshed)="changePage()" [dataSize]="dataSource.totalResult"
|
<app-refresh-table *ngIf="project" (refreshed)="changePage()" [dataSize]="dataSource.totalResult"
|
||||||
[selection]="selection">
|
[selection]="selection" [loading]="dataSource?.loading$ | async">
|
||||||
<ng-template appHasRole actions
|
<ng-template appHasRole actions
|
||||||
[appHasRole]="['project.member.delete:' + project.projectId, 'project.member.delete']">
|
[appHasRole]="['project.member.delete:' + project.projectId, 'project.member.delete']">
|
||||||
<button (click)="removeProjectMemberSelection()" color="warn"
|
<button (click)="removeProjectMemberSelection()" color="warn"
|
||||||
@@ -29,9 +29,6 @@
|
|||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
<div class="table-wrapper">
|
<div class="table-wrapper">
|
||||||
<div class="spinner-container" *ngIf="dataSource?.loading$ | async">
|
|
||||||
<mat-spinner diameter="50"></mat-spinner>
|
|
||||||
</div>
|
|
||||||
<table mat-table class="background-style full-width-table" aria-label="Elements"
|
<table mat-table class="background-style full-width-table" aria-label="Elements"
|
||||||
[dataSource]="dataSource">
|
[dataSource]="dataSource">
|
||||||
<ng-container matColumnDef="select">
|
<ng-container matColumnDef="select">
|
||||||
|
@@ -54,12 +54,6 @@
|
|||||||
.table-wrapper {
|
.table-wrapper {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
|
||||||
.spinner-container {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
table, mat-paginator {
|
table, mat-paginator {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
<app-refresh-table *ngIf="projectId" (refreshed)="refreshPage()" [dataSize]="dataSource.totalResult"
|
<app-refresh-table *ngIf="projectId" (refreshed)="refreshPage()" [dataSize]="dataSource.totalResult"
|
||||||
[selection]="selection">
|
[selection]="selection" [loading]="dataSource.loading$ | async">
|
||||||
<ng-template appHasRole [appHasRole]="['project.role.delete']" actions>
|
<ng-template appHasRole [appHasRole]="['project.role.delete']" actions>
|
||||||
<button color="warn" class="icon-button" [disabled]="disabled"
|
<button color="warn" class="icon-button" [disabled]="disabled"
|
||||||
matTooltip="{{'PROJECT.ROLE.DELETE' | translate}}" (click)="deleteSelectedRoles()" mat-icon-button
|
matTooltip="{{'PROJECT.ROLE.DELETE' | translate}}" (click)="deleteSelectedRoles()" mat-icon-button
|
||||||
@@ -15,10 +15,6 @@
|
|||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
<div class="table-wrapper">
|
<div class="table-wrapper">
|
||||||
<div class="spinner-container" *ngIf="dataSource.loading$ | async">
|
|
||||||
<mat-spinner diameter="50"></mat-spinner>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<table [dataSource]="dataSource" mat-table class="full-width-table" matSort aria-label="Elements">
|
<table [dataSource]="dataSource" mat-table class="full-width-table" matSort aria-label="Elements">
|
||||||
<ng-container matColumnDef="select">
|
<ng-container matColumnDef="select">
|
||||||
<th class="selection" mat-header-cell *matHeaderCellDef>
|
<th class="selection" mat-header-cell *matHeaderCellDef>
|
||||||
|
@@ -10,51 +10,45 @@
|
|||||||
.table-wrapper {
|
.table-wrapper {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
|
||||||
.spinner-container {
|
table, mat-paginator {
|
||||||
display: flex;
|
width: 100%;
|
||||||
align-items: center;
|
background-color: #2d2e30;
|
||||||
justify-content: center;
|
|
||||||
|
td, th {
|
||||||
|
&:first-child {
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.action {
|
||||||
|
width: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.data-row {
|
||||||
|
&:hover {
|
||||||
|
background-color: #ffffff05;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.selection {
|
||||||
|
width: 50px;
|
||||||
|
max-width: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.margin-neg {
|
||||||
|
margin-left: -1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.role {
|
||||||
|
display: inline-block;
|
||||||
|
margin: .25rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
table, mat-paginator {
|
|
||||||
width: 100%;
|
|
||||||
background-color: #2d2e30;
|
|
||||||
|
|
||||||
td, th {
|
|
||||||
&:first-child {
|
|
||||||
padding-left: 0;
|
|
||||||
padding-right: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
padding-right: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.action {
|
|
||||||
width: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.data-row {
|
|
||||||
&:hover {
|
|
||||||
background-color: #ffffff05;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.selection {
|
|
||||||
width: 50px;
|
|
||||||
max-width: 50px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.margin-neg {
|
|
||||||
margin-left: -1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.role {
|
|
||||||
display: inline-block;
|
|
||||||
margin: .25rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.pointer {
|
.pointer {
|
||||||
|
@@ -10,6 +10,7 @@
|
|||||||
</ng-container>
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
<span class="fill-space"></span>
|
<span class="fill-space"></span>
|
||||||
|
<mat-spinner *ngIf="loading" diameter="20"></mat-spinner>
|
||||||
<button mat-icon-button (click)="emitRefresh()" class="icon-button" matTooltip="{{'ACTIONS.REFRESH' | translate}}">
|
<button mat-icon-button (click)="emitRefresh()" class="icon-button" matTooltip="{{'ACTIONS.REFRESH' | translate}}">
|
||||||
<mat-icon>refresh</mat-icon>
|
<mat-icon>refresh</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
|
@@ -16,6 +16,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mat-spinner {
|
||||||
|
margin-top: 2px;
|
||||||
|
margin-bottom: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
.fill-space {
|
.fill-space {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
@@ -29,6 +29,7 @@ export class RefreshTableComponent implements OnInit {
|
|||||||
@Input() public selection: SelectionModel<any> = new SelectionModel<any>(true, []);
|
@Input() public selection: SelectionModel<any> = new SelectionModel<any>(true, []);
|
||||||
@Input() public dataSize: number = 0;
|
@Input() public dataSize: number = 0;
|
||||||
@Input() public emitRefreshAfterTimeoutInMs: number = 0;
|
@Input() public emitRefreshAfterTimeoutInMs: number = 0;
|
||||||
|
@Input() public loading: boolean = false;
|
||||||
@Output() public refreshed: EventEmitter<void> = new EventEmitter();
|
@Output() public refreshed: EventEmitter<void> = new EventEmitter();
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
@@ -3,6 +3,7 @@ import { NgModule } from '@angular/core';
|
|||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms';
|
||||||
import { MatButtonModule } from '@angular/material/button';
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
import { MatIconModule } from '@angular/material/icon';
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
|
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
|
||||||
@@ -19,6 +20,7 @@ import { RefreshTableComponent } from './refresh-table.component';
|
|||||||
TranslateModule,
|
TranslateModule,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
MatTooltipModule,
|
MatTooltipModule,
|
||||||
|
MatProgressSpinnerModule,
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
RefreshTableComponent,
|
RefreshTableComponent,
|
||||||
|
@@ -7,11 +7,12 @@
|
|||||||
font-size: .8rem;
|
font-size: .8rem;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: white;
|
color: #4072b4;
|
||||||
&:hover {
|
&:hover {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
color: white;
|
color: #6992c9;
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
<app-refresh-table (refreshed)="changePage()" [dataSize]="dataSource.totalResult" [selection]="selection">
|
<app-refresh-table [loading]="dataSource?.loading$ | async" (refreshed)="changePage()"
|
||||||
|
[dataSize]="dataSource.totalResult" [selection]="selection">
|
||||||
<button color="warn" matTooltip="{{'GRANTS.DELETE' | translate}}" class="icon-button" mat-icon-button actions
|
<button color="warn" matTooltip="{{'GRANTS.DELETE' | translate}}" class="icon-button" mat-icon-button actions
|
||||||
(click)="deleteGrantSelection()" *ngIf="selection.hasValue() && allowDelete">
|
(click)="deleteGrantSelection()" *ngIf="selection.hasValue() && allowDelete">
|
||||||
<i class="las la-trash"></i>
|
<i class="las la-trash"></i>
|
||||||
@@ -9,9 +10,6 @@
|
|||||||
</a>
|
</a>
|
||||||
|
|
||||||
<div class="table-wrapper">
|
<div class="table-wrapper">
|
||||||
<div class="spinner-container" *ngIf="dataSource.loading$ | async">
|
|
||||||
<mat-spinner diameter="50"></mat-spinner>
|
|
||||||
</div>
|
|
||||||
<table mat-table multiTemplateDataRows class="full-width-table" aria-label="Elements" [dataSource]="dataSource">
|
<table mat-table multiTemplateDataRows class="full-width-table" aria-label="Elements" [dataSource]="dataSource">
|
||||||
<ng-container matColumnDef="select">
|
<ng-container matColumnDef="select">
|
||||||
<th class="selection" mat-header-cell *matHeaderCellDef>
|
<th class="selection" mat-header-cell *matHeaderCellDef>
|
||||||
|
@@ -6,12 +6,6 @@
|
|||||||
.table-wrapper {
|
.table-wrapper {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
|
||||||
.spinner-container {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
table, mat-paginator {
|
table, mat-paginator {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background-color: #2d2e30;
|
background-color: #2d2e30;
|
||||||
|
@@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<ng-template appHasRole [appHasRole]="['iam.write']">
|
<ng-template appHasRole [appHasRole]="['iam.write']">
|
||||||
<div class="item card">
|
<app-card class="item">
|
||||||
<div class="top">
|
<div class="top">
|
||||||
<h2>
|
<h2>
|
||||||
<i class="icon las la-gem"></i>
|
<i class="icon las la-gem"></i>
|
||||||
@@ -18,12 +18,12 @@
|
|||||||
</div>
|
</div>
|
||||||
<span class="fill-space"></span>
|
<span class="fill-space"></span>
|
||||||
<div class="footer">
|
<div class="footer">
|
||||||
<a color="primary" mat-button [routerLink]="['/iam']">{{'HOME.IAM_BUTTON' | translate}}</a>
|
<a color="primary" mat-stroked-button [routerLink]="['/iam']">{{'HOME.IAM_BUTTON' | translate}}</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</app-card>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
<div class="item card">
|
<app-card class="item">
|
||||||
<div class="top">
|
<div class="top">
|
||||||
<h2> <i class="icon las la-user-circle"></i>
|
<h2> <i class="icon las la-user-circle"></i>
|
||||||
{{'HOME.SECURITYANDPRIVACY'| translate}}</h2>
|
{{'HOME.SECURITYANDPRIVACY'| translate}}</h2>
|
||||||
@@ -31,13 +31,13 @@
|
|||||||
</div>
|
</div>
|
||||||
<span class="fill-space"></span>
|
<span class="fill-space"></span>
|
||||||
<div class="footer">
|
<div class="footer">
|
||||||
<a color="primary" mat-button
|
<a color="primary" mat-stroked-button
|
||||||
[routerLink]="['/users/me']">{{'HOME.SECURITYANDPRIVACY_BUTTON' | translate}}</a>
|
[routerLink]="['/users/me']">{{'HOME.SECURITYANDPRIVACY_BUTTON' | translate}}</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</app-card>
|
||||||
|
|
||||||
<ng-template appHasRole [appHasRole]="['project.read']">
|
<ng-template appHasRole [appHasRole]="['project.read']">
|
||||||
<div class="item card">
|
<app-card class="item">
|
||||||
<div class="top">
|
<div class="top">
|
||||||
<h2>
|
<h2>
|
||||||
<i class="icon las la-layer-group"></i>
|
<i class="icon las la-layer-group"></i>
|
||||||
@@ -46,14 +46,14 @@
|
|||||||
</div>
|
</div>
|
||||||
<span class="fill-space"></span>
|
<span class="fill-space"></span>
|
||||||
<div class="footer">
|
<div class="footer">
|
||||||
<a color="primary" mat-button
|
<a color="primary" mat-stroked-button
|
||||||
[routerLink]="['/projects']">{{'HOME.PROJECTS_BUTTON' | translate}}</a>
|
[routerLink]="['/projects']">{{'HOME.PROJECTS_BUTTON' | translate}}</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</app-card>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
<ng-template appHasRole [appHasRole]="['org.read']">
|
<ng-template appHasRole [appHasRole]="['org.read']">
|
||||||
<div class="item card">
|
<app-card class="item">
|
||||||
<div class="top">
|
<div class="top">
|
||||||
<h2> <i class="icon las la-archway"></i>
|
<h2> <i class="icon las la-archway"></i>
|
||||||
{{'HOME.PROTECTION'| translate}}</h2>
|
{{'HOME.PROTECTION'| translate}}</h2>
|
||||||
@@ -61,13 +61,14 @@
|
|||||||
</div>
|
</div>
|
||||||
<span class="fill-space"></span>
|
<span class="fill-space"></span>
|
||||||
<div class="footer">
|
<div class="footer">
|
||||||
<a color="primary" mat-button [routerLink]="['/org']">{{'HOME.PROTECTION_BUTTON' | translate}}</a>
|
<a color="primary" mat-stroked-button
|
||||||
|
[routerLink]="['/org']">{{'HOME.PROTECTION_BUTTON' | translate}}</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</app-card>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
<ng-template appHasRole [appHasRole]="['user.read']">
|
<ng-template appHasRole [appHasRole]="['user.read']">
|
||||||
<div class="item card">
|
<app-card class="item">
|
||||||
<div class="top">
|
<div class="top">
|
||||||
<h2>
|
<h2>
|
||||||
<i class="las la-crosshairs"></i>
|
<i class="las la-crosshairs"></i>
|
||||||
@@ -78,7 +79,7 @@
|
|||||||
<div class="footer">
|
<div class="footer">
|
||||||
<a color="primary" mat-button [routerLink]="['/users/me']">{{'HOME.USERS_BUTTON' | translate}}</a>
|
<a color="primary" mat-button [routerLink]="['/users/me']">{{'HOME.USERS_BUTTON' | translate}}</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</app-card>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
@@ -45,16 +45,11 @@
|
|||||||
|
|
||||||
.item {
|
.item {
|
||||||
flex: 1 1 45%;
|
flex: 1 1 45%;
|
||||||
// box-sizing: border-box;
|
margin: 0 1rem;
|
||||||
margin: 1rem;
|
|
||||||
border: 1px solid #ffffff20;
|
|
||||||
border-radius: .5rem;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
transition: border-color .1s;
|
|
||||||
|
|
||||||
.top {
|
.top {
|
||||||
padding: 1rem 2rem;
|
|
||||||
h2 {
|
h2 {
|
||||||
margin-top: .5rem;
|
margin-top: .5rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -79,11 +74,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.footer {
|
.footer {
|
||||||
height: 60px;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 0 1rem;
|
|
||||||
border-top: 1px solid #ffffff20;
|
border-top: 1px solid #ffffff20;
|
||||||
|
padding-top: 1rem;
|
||||||
|
|
||||||
a {
|
a {
|
||||||
border-radius: .5rem;
|
border-radius: .5rem;
|
||||||
|
@@ -4,6 +4,7 @@ import { MatButtonModule } from '@angular/material/button';
|
|||||||
import { MatIconModule } from '@angular/material/icon';
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
|
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
|
||||||
|
import { CardModule } from 'src/app/modules/card/card.module';
|
||||||
import { SharedModule } from 'src/app/modules/shared/shared.module';
|
import { SharedModule } from 'src/app/modules/shared/shared.module';
|
||||||
|
|
||||||
import { HomeRoutingModule } from './home-routing.module';
|
import { HomeRoutingModule } from './home-routing.module';
|
||||||
@@ -21,6 +22,7 @@ import { HomeComponent } from './home.component';
|
|||||||
MatButtonModule,
|
MatButtonModule,
|
||||||
TranslateModule,
|
TranslateModule,
|
||||||
SharedModule,
|
SharedModule,
|
||||||
|
CardModule,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class HomeModule { }
|
export class HomeModule { }
|
||||||
|
@@ -1,28 +0,0 @@
|
|||||||
<div class="groups">
|
|
||||||
<span class="header">{{ 'IAM.MEMBER.TITLE' | translate }}</span>
|
|
||||||
<span class="sub-header">{{ 'IAM.MEMBER.DESCRIPTION' | translate }}</span>
|
|
||||||
<div class="people">
|
|
||||||
<div class="img-list">
|
|
||||||
<ng-container *ngIf="totalResult < 10; else compact">
|
|
||||||
<ng-container *ngFor="let member of membersSubject | async; index as i">
|
|
||||||
<div (click)="showDetail()" class="avatar-circle" [ngStyle]="{'z-index': 100 - i}"
|
|
||||||
matTooltip="{{ member.email }} | {{member.rolesList?.join(' ')}}">
|
|
||||||
<app-avatar *ngIf="member && (member.displayName || (member.firstName && member.lastName))"
|
|
||||||
class="avatar dontcloseonclick"
|
|
||||||
[name]="member.displayName ? member.displayName : (member.firstName + ' '+ member.lastName)"
|
|
||||||
[size]="32">
|
|
||||||
</app-avatar>
|
|
||||||
</div>
|
|
||||||
</ng-container>
|
|
||||||
</ng-container>
|
|
||||||
<ng-template #compact>
|
|
||||||
<div (click)="showDetail()" class="avatar-circle" matTooltip="Click to show detail">
|
|
||||||
<span>{{totalResult}}</span>
|
|
||||||
</div>
|
|
||||||
</ng-template>
|
|
||||||
<button class="add-img" (click)="openAddMember()" mat-icon-button aria-label="Edit contributors">
|
|
||||||
<mat-icon>add</mat-icon>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
@@ -1,74 +0,0 @@
|
|||||||
.groups {
|
|
||||||
padding-top: 1rem;
|
|
||||||
|
|
||||||
.header {
|
|
||||||
display: block;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
font-weight: 400;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sub-header {
|
|
||||||
font-size: .8rem;
|
|
||||||
color: #8795a1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.people {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
|
|
||||||
.owner {
|
|
||||||
margin-right: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.img-list {
|
|
||||||
width: 100%;
|
|
||||||
margin-top: 0.5rem;
|
|
||||||
margin-left: 1rem;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.avatar-img, .avatar-circle {
|
|
||||||
float: left;
|
|
||||||
margin: 0 8px 0 -15px;
|
|
||||||
height: 32px;
|
|
||||||
width: 32px;
|
|
||||||
border-radius: 50%;
|
|
||||||
-webkit-box-shadow: 2px 0px 7px -1px rgba(33,34,36,.5);
|
|
||||||
-moz-box-shadow: 2px 0px 7px -1px rgba(33,34,36,.5);
|
|
||||||
box-shadow: 2px 0px 7px -1px rgba(33,34,36,.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.add-img {
|
|
||||||
float: left;
|
|
||||||
margin: 0 8px 0 -15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.avatar-img {
|
|
||||||
&:before {
|
|
||||||
content: '';
|
|
||||||
display: block;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
bottom: 0;
|
|
||||||
height: 32px;
|
|
||||||
width: 32px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.avatar-circle {
|
|
||||||
cursor: pointer;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
background-color: indianred;
|
|
||||||
}
|
|
||||||
|
|
||||||
.margin-neg {
|
|
||||||
margin-left: -1rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@@ -1,34 +0,0 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
|
||||||
import { MatPaginatorModule } from '@angular/material/paginator';
|
|
||||||
import { MatSortModule } from '@angular/material/sort';
|
|
||||||
import { MatTableModule } from '@angular/material/table';
|
|
||||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
|
||||||
|
|
||||||
import { IamContributorsComponent } from './iam-contributors.component';
|
|
||||||
|
|
||||||
describe('OrgContributorsComponent', () => {
|
|
||||||
let component: IamContributorsComponent;
|
|
||||||
let fixture: ComponentFixture<IamContributorsComponent>;
|
|
||||||
|
|
||||||
beforeEach(async(() => {
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
declarations: [IamContributorsComponent],
|
|
||||||
imports: [
|
|
||||||
NoopAnimationsModule,
|
|
||||||
MatPaginatorModule,
|
|
||||||
MatSortModule,
|
|
||||||
MatTableModule,
|
|
||||||
],
|
|
||||||
}).compileComponents();
|
|
||||||
}));
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
fixture = TestBed.createComponent(IamContributorsComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should compile', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
@@ -1,89 +0,0 @@
|
|||||||
import { Component, Input, OnInit, ViewChild } from '@angular/core';
|
|
||||||
import { MatDialog } from '@angular/material/dialog';
|
|
||||||
import { MatPaginator } from '@angular/material/paginator';
|
|
||||||
import { MatTable } from '@angular/material/table';
|
|
||||||
import { Router } from '@angular/router';
|
|
||||||
import { BehaviorSubject, from, Observable, of } from 'rxjs';
|
|
||||||
import { catchError, finalize, map } from 'rxjs/operators';
|
|
||||||
import { OrgMember, OrgMemberView, OrgState, User } from 'src/app/proto/generated/management_pb';
|
|
||||||
import { AdminService } from 'src/app/services/admin.service';
|
|
||||||
import { ToastService } from 'src/app/services/toast.service';
|
|
||||||
|
|
||||||
import {
|
|
||||||
CreationType,
|
|
||||||
MemberCreateDialogComponent,
|
|
||||||
} from '../../../modules/add-member-dialog/member-create-dialog.component';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-iam-contributors',
|
|
||||||
templateUrl: './iam-contributors.component.html',
|
|
||||||
styleUrls: ['./iam-contributors.component.scss'],
|
|
||||||
})
|
|
||||||
export class IamContributorsComponent implements OnInit {
|
|
||||||
@Input() public disabled: boolean = false;
|
|
||||||
@ViewChild(MatPaginator) public paginator!: MatPaginator;
|
|
||||||
@ViewChild(MatTable) public table!: MatTable<OrgMember.AsObject>;
|
|
||||||
/** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */
|
|
||||||
public displayedColumns: string[] = ['select', 'firstname', 'lastname', 'username', 'email', 'roles'];
|
|
||||||
|
|
||||||
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
|
||||||
public loading$: Observable<boolean> = this.loadingSubject.asObservable();
|
|
||||||
public totalResult: number = 0;
|
|
||||||
public membersSubject: BehaviorSubject<OrgMemberView.AsObject[]>
|
|
||||||
= new BehaviorSubject<OrgMemberView.AsObject[]>([]);
|
|
||||||
|
|
||||||
public OrgState: any = OrgState;
|
|
||||||
constructor(private adminService: AdminService, private dialog: MatDialog,
|
|
||||||
private toast: ToastService,
|
|
||||||
private router: Router) { }
|
|
||||||
|
|
||||||
public ngOnInit(): void {
|
|
||||||
this.loadMembers(0, 25, 'asc');
|
|
||||||
}
|
|
||||||
|
|
||||||
public loadMembers(pageIndex: number, pageSize: number, sortDirection?: string): void {
|
|
||||||
const offset = pageIndex * pageSize;
|
|
||||||
|
|
||||||
this.loadingSubject.next(true);
|
|
||||||
from(this.adminService.SearchIamMembers(pageSize, offset)).pipe(
|
|
||||||
map(resp => {
|
|
||||||
this.totalResult = resp.toObject().totalResult;
|
|
||||||
return resp.toObject().resultList;
|
|
||||||
}),
|
|
||||||
catchError(() => of([])),
|
|
||||||
finalize(() => this.loadingSubject.next(false)),
|
|
||||||
).subscribe(members => {
|
|
||||||
this.membersSubject.next(members);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public openAddMember(): void {
|
|
||||||
const dialogRef = this.dialog.open(MemberCreateDialogComponent, {
|
|
||||||
data: {
|
|
||||||
creationType: CreationType.IAM,
|
|
||||||
},
|
|
||||||
width: '400px',
|
|
||||||
});
|
|
||||||
|
|
||||||
dialogRef.afterClosed().subscribe(resp => {
|
|
||||||
if (resp) {
|
|
||||||
const users: User.AsObject[] = resp.users;
|
|
||||||
const roles: string[] = resp.roles;
|
|
||||||
|
|
||||||
if (users && users.length && roles && roles.length) {
|
|
||||||
Promise.all(users.map(user => {
|
|
||||||
return this.adminService.AddIamMember(user.id, roles);
|
|
||||||
})).then(() => {
|
|
||||||
this.toast.showInfo('IAM.TOAST.MEMBERADDED');
|
|
||||||
}).catch(error => {
|
|
||||||
this.toast.showError(error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public showDetail(): void {
|
|
||||||
this.router.navigate(['iam/members']);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,44 +0,0 @@
|
|||||||
import { CommonModule } from '@angular/common';
|
|
||||||
import { NgModule } from '@angular/core';
|
|
||||||
import { FormsModule } from '@angular/forms';
|
|
||||||
import { MatButtonModule } from '@angular/material/button';
|
|
||||||
import { MatCheckboxModule } from '@angular/material/checkbox';
|
|
||||||
import { MatDialogModule } from '@angular/material/dialog';
|
|
||||||
import { MatIconModule } from '@angular/material/icon';
|
|
||||||
import { MatPaginatorModule } from '@angular/material/paginator';
|
|
||||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
|
||||||
import { MatTableModule } from '@angular/material/table';
|
|
||||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
|
||||||
import { RouterModule } from '@angular/router';
|
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
|
||||||
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
|
|
||||||
import { AvatarModule } from 'src/app/modules/avatar/avatar.module';
|
|
||||||
|
|
||||||
import { MemberCreateDialogModule } from '../../../modules/add-member-dialog/member-create-dialog.module';
|
|
||||||
import { IamContributorsComponent } from './iam-contributors.component';
|
|
||||||
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
declarations: [IamContributorsComponent],
|
|
||||||
imports: [
|
|
||||||
CommonModule,
|
|
||||||
FormsModule,
|
|
||||||
MemberCreateDialogModule,
|
|
||||||
HasRoleModule,
|
|
||||||
MatButtonModule,
|
|
||||||
MatDialogModule,
|
|
||||||
MatTableModule,
|
|
||||||
MatPaginatorModule,
|
|
||||||
MatIconModule,
|
|
||||||
RouterModule,
|
|
||||||
AvatarModule,
|
|
||||||
MatProgressSpinnerModule,
|
|
||||||
MatCheckboxModule,
|
|
||||||
MatTooltipModule,
|
|
||||||
TranslateModule,
|
|
||||||
],
|
|
||||||
exports: [
|
|
||||||
IamContributorsComponent,
|
|
||||||
],
|
|
||||||
})
|
|
||||||
export class IamContributorsModule { }
|
|
@@ -9,7 +9,9 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="side" metainfo>
|
<div class="side" metainfo>
|
||||||
<app-iam-contributors>
|
<app-contributors [totalResult]="totalMemberResult" [membersSubject]="membersSubject"
|
||||||
</app-iam-contributors>
|
title="{{ 'PROJECT.MEMBER.TITLE' | translate }}" description="{{ 'PROJECT.MEMBER.TITLEDESC' | translate }}"
|
||||||
|
(addClicked)="openAddMember()" (showDetailClicked)="showDetail()" [disabled]="false">
|
||||||
|
</app-contributors>
|
||||||
</div>
|
</div>
|
||||||
</app-meta-layout>
|
</app-meta-layout>
|
@@ -1,4 +1,12 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { BehaviorSubject, from, Observable, of } from 'rxjs';
|
||||||
|
import { catchError, finalize, map } from 'rxjs/operators';
|
||||||
|
import { CreationType, MemberCreateDialogComponent } from 'src/app/modules/add-member-dialog/member-create-dialog.component';
|
||||||
|
import { OrgMemberView, User } from 'src/app/proto/generated/management_pb';
|
||||||
|
import { AdminService } from 'src/app/services/admin.service';
|
||||||
|
import { ToastService } from 'src/app/services/toast.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-iam',
|
selector: 'app-iam',
|
||||||
@@ -6,4 +14,54 @@ import { Component } from '@angular/core';
|
|||||||
styleUrls: ['./iam.component.scss'],
|
styleUrls: ['./iam.component.scss'],
|
||||||
})
|
})
|
||||||
export class IamComponent {
|
export class IamComponent {
|
||||||
|
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||||
|
public loading$: Observable<boolean> = this.loadingSubject.asObservable();
|
||||||
|
public totalMemberResult: number = 0;
|
||||||
|
public membersSubject: BehaviorSubject<OrgMemberView.AsObject[]>
|
||||||
|
= new BehaviorSubject<OrgMemberView.AsObject[]>([]);
|
||||||
|
|
||||||
|
constructor(private adminService: AdminService, private dialog: MatDialog, private toast: ToastService,
|
||||||
|
private router: Router) {
|
||||||
|
this.loadingSubject.next(true);
|
||||||
|
from(this.adminService.SearchIamMembers(100, 0)).pipe(
|
||||||
|
map(resp => {
|
||||||
|
this.totalMemberResult = resp.toObject().totalResult;
|
||||||
|
return resp.toObject().resultList;
|
||||||
|
}),
|
||||||
|
catchError(() => of([])),
|
||||||
|
finalize(() => this.loadingSubject.next(false)),
|
||||||
|
).subscribe(members => {
|
||||||
|
this.membersSubject.next(members);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public openAddMember(): void {
|
||||||
|
const dialogRef = this.dialog.open(MemberCreateDialogComponent, {
|
||||||
|
data: {
|
||||||
|
creationType: CreationType.IAM,
|
||||||
|
},
|
||||||
|
width: '400px',
|
||||||
|
});
|
||||||
|
|
||||||
|
dialogRef.afterClosed().subscribe(resp => {
|
||||||
|
if (resp) {
|
||||||
|
const users: User.AsObject[] = resp.users;
|
||||||
|
const roles: string[] = resp.roles;
|
||||||
|
|
||||||
|
if (users && users.length && roles && roles.length) {
|
||||||
|
Promise.all(users.map(user => {
|
||||||
|
return this.adminService.AddIamMember(user.id, roles);
|
||||||
|
})).then(() => {
|
||||||
|
this.toast.showInfo('IAM.TOAST.MEMBERADDED');
|
||||||
|
}).catch(error => {
|
||||||
|
this.toast.showError(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public showDetail(): void {
|
||||||
|
this.router.navigate(['iam/members']);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -18,12 +18,12 @@ import { TranslateModule } from '@ngx-translate/core';
|
|||||||
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
|
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
|
||||||
import { CardModule } from 'src/app/modules/card/card.module';
|
import { CardModule } from 'src/app/modules/card/card.module';
|
||||||
import { ChangesModule } from 'src/app/modules/changes/changes.module';
|
import { ChangesModule } from 'src/app/modules/changes/changes.module';
|
||||||
|
import { ContributorsModule } from 'src/app/modules/contributors/contributors.module';
|
||||||
import { MetaLayoutModule } from 'src/app/modules/meta-layout/meta-layout.module';
|
import { MetaLayoutModule } from 'src/app/modules/meta-layout/meta-layout.module';
|
||||||
import { SharedModule } from 'src/app/modules/shared/shared.module';
|
import { SharedModule } from 'src/app/modules/shared/shared.module';
|
||||||
import { LocalizedDatePipeModule } from 'src/app/pipes/localized-date-pipe.module';
|
import { LocalizedDatePipeModule } from 'src/app/pipes/localized-date-pipe.module';
|
||||||
import { TimestampToDatePipeModule } from 'src/app/pipes/timestamp-to-date-pipe.module';
|
import { TimestampToDatePipeModule } from 'src/app/pipes/timestamp-to-date-pipe.module';
|
||||||
|
|
||||||
import { IamContributorsModule } from './iam-contributors/iam-contributors.module';
|
|
||||||
import { IamRoutingModule } from './iam-routing.module';
|
import { IamRoutingModule } from './iam-routing.module';
|
||||||
import { IamViewsComponent } from './iam-views/iam-views.component';
|
import { IamViewsComponent } from './iam-views/iam-views.component';
|
||||||
import { IamComponent } from './iam.component';
|
import { IamComponent } from './iam.component';
|
||||||
@@ -55,7 +55,7 @@ import { IamComponent } from './iam.component';
|
|||||||
FormsModule,
|
FormsModule,
|
||||||
TranslateModule,
|
TranslateModule,
|
||||||
MatDialogModule,
|
MatDialogModule,
|
||||||
IamContributorsModule,
|
ContributorsModule,
|
||||||
LocalizedDatePipeModule,
|
LocalizedDatePipeModule,
|
||||||
TimestampToDatePipeModule,
|
TimestampToDatePipeModule,
|
||||||
SharedModule,
|
SharedModule,
|
||||||
|
@@ -1,74 +0,0 @@
|
|||||||
.groups {
|
|
||||||
padding-top: 1rem;
|
|
||||||
|
|
||||||
.header {
|
|
||||||
display: block;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
font-weight: 400;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sub-header {
|
|
||||||
font-size: .8rem;
|
|
||||||
color: #8795a1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.people {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
|
|
||||||
.owner {
|
|
||||||
margin-right: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.img-list {
|
|
||||||
width: 100%;
|
|
||||||
margin-top: 0.5rem;
|
|
||||||
margin-left: 1rem;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.avatar-circle {
|
|
||||||
float: left;
|
|
||||||
margin: 0 8px 0 -15px;
|
|
||||||
height: 32px;
|
|
||||||
width: 32px;
|
|
||||||
border-radius: 50%;
|
|
||||||
-webkit-box-shadow: 2px 0px 7px -1px rgba(33,34,36,.5);
|
|
||||||
-moz-box-shadow: 2px 0px 7px -1px rgba(33,34,36,.5);
|
|
||||||
box-shadow: 2px 0px 7px -1px rgba(33,34,36,.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.add-img {
|
|
||||||
float: left;
|
|
||||||
margin: 0 8px 0 -15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.avatar-img {
|
|
||||||
&:before {
|
|
||||||
content: '';
|
|
||||||
display: block;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
bottom: 0;
|
|
||||||
height: 32px;
|
|
||||||
width: 32px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.avatar-circle {
|
|
||||||
cursor: pointer;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
background-color: indianred;
|
|
||||||
}
|
|
||||||
|
|
||||||
.margin-neg {
|
|
||||||
margin-left: -1rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@@ -1,34 +0,0 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
|
||||||
import { MatPaginatorModule } from '@angular/material/paginator';
|
|
||||||
import { MatSortModule } from '@angular/material/sort';
|
|
||||||
import { MatTableModule } from '@angular/material/table';
|
|
||||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
|
||||||
|
|
||||||
import { OrgContributorsComponent } from './org-contributors.component';
|
|
||||||
|
|
||||||
describe('OrgContributorsComponent', () => {
|
|
||||||
let component: OrgContributorsComponent;
|
|
||||||
let fixture: ComponentFixture<OrgContributorsComponent>;
|
|
||||||
|
|
||||||
beforeEach(async(() => {
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
declarations: [OrgContributorsComponent],
|
|
||||||
imports: [
|
|
||||||
NoopAnimationsModule,
|
|
||||||
MatPaginatorModule,
|
|
||||||
MatSortModule,
|
|
||||||
MatTableModule,
|
|
||||||
],
|
|
||||||
}).compileComponents();
|
|
||||||
}));
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
fixture = TestBed.createComponent(OrgContributorsComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should compile', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
@@ -1,92 +0,0 @@
|
|||||||
import { Component, Input, OnInit, ViewChild } from '@angular/core';
|
|
||||||
import { MatDialog } from '@angular/material/dialog';
|
|
||||||
import { MatPaginator } from '@angular/material/paginator';
|
|
||||||
import { MatTable } from '@angular/material/table';
|
|
||||||
import { Router } from '@angular/router';
|
|
||||||
import { BehaviorSubject, from, Observable, of } from 'rxjs';
|
|
||||||
import { catchError, finalize, map } from 'rxjs/operators';
|
|
||||||
import { Org, OrgMemberView, OrgState, User } from 'src/app/proto/generated/management_pb';
|
|
||||||
import { OrgService } from 'src/app/services/org.service';
|
|
||||||
import { ToastService } from 'src/app/services/toast.service';
|
|
||||||
|
|
||||||
import {
|
|
||||||
CreationType,
|
|
||||||
MemberCreateDialogComponent,
|
|
||||||
} from '../../../modules/add-member-dialog/member-create-dialog.component';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-org-contributors',
|
|
||||||
templateUrl: './org-contributors.component.html',
|
|
||||||
styleUrls: ['./org-contributors.component.scss'],
|
|
||||||
})
|
|
||||||
export class OrgContributorsComponent implements OnInit {
|
|
||||||
@Input() public org!: Org.AsObject;
|
|
||||||
@Input() public disabled: boolean = false;
|
|
||||||
@ViewChild(MatPaginator) public paginator!: MatPaginator;
|
|
||||||
@ViewChild(MatTable) public table!: MatTable<OrgMemberView.AsObject>;
|
|
||||||
/** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */
|
|
||||||
public displayedColumns: string[] = ['select', 'firstname', 'lastname', 'username', 'email', 'roles'];
|
|
||||||
|
|
||||||
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
|
||||||
public loading$: Observable<boolean> = this.loadingSubject.asObservable();
|
|
||||||
public totalResult: number = 0;
|
|
||||||
public membersSubject: BehaviorSubject<OrgMemberView.AsObject[]>
|
|
||||||
= new BehaviorSubject<OrgMemberView.AsObject[]>([]);
|
|
||||||
|
|
||||||
public OrgState: any = OrgState;
|
|
||||||
constructor(private orgService: OrgService, private dialog: MatDialog,
|
|
||||||
private toast: ToastService,
|
|
||||||
private router: Router) { }
|
|
||||||
|
|
||||||
public ngOnInit(): void {
|
|
||||||
this.loadMembers(0, 25, 'asc');
|
|
||||||
}
|
|
||||||
|
|
||||||
public loadMembers(pageIndex: number, pageSize: number, sortDirection?: string): void {
|
|
||||||
const offset = pageIndex * pageSize;
|
|
||||||
|
|
||||||
this.loadingSubject.next(true);
|
|
||||||
from(this.orgService.SearchMyOrgMembers(pageSize, offset)).pipe(
|
|
||||||
map(resp => {
|
|
||||||
this.totalResult = resp.toObject().totalResult;
|
|
||||||
return resp.toObject().resultList;
|
|
||||||
}),
|
|
||||||
catchError(() => of([])),
|
|
||||||
finalize(() => this.loadingSubject.next(false)),
|
|
||||||
).subscribe(members => {
|
|
||||||
this.membersSubject.next(members);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public openAddMember(): void {
|
|
||||||
const dialogRef = this.dialog.open(MemberCreateDialogComponent, {
|
|
||||||
data: {
|
|
||||||
creationType: CreationType.ORG,
|
|
||||||
},
|
|
||||||
width: '400px',
|
|
||||||
});
|
|
||||||
|
|
||||||
dialogRef.afterClosed().subscribe(resp => {
|
|
||||||
if (resp) {
|
|
||||||
const users: User.AsObject[] = resp.users;
|
|
||||||
const roles: string[] = resp.roles;
|
|
||||||
|
|
||||||
if (users && users.length && roles && roles.length) {
|
|
||||||
Promise.all(users.map(user => {
|
|
||||||
return this.orgService.AddMyOrgMember(user.id, roles);
|
|
||||||
})).then(() => {
|
|
||||||
this.toast.showInfo('ORG.TOAST.MEMBERADDED', true);
|
|
||||||
}).catch(error => {
|
|
||||||
this.toast.showError(error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public showDetail(): void {
|
|
||||||
if (this.org?.state === OrgState.ORGSTATE_ACTIVE) {
|
|
||||||
this.router.navigate(['org/members']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,44 +0,0 @@
|
|||||||
import { CommonModule } from '@angular/common';
|
|
||||||
import { NgModule } from '@angular/core';
|
|
||||||
import { FormsModule } from '@angular/forms';
|
|
||||||
import { MatButtonModule } from '@angular/material/button';
|
|
||||||
import { MatCheckboxModule } from '@angular/material/checkbox';
|
|
||||||
import { MatDialogModule } from '@angular/material/dialog';
|
|
||||||
import { MatIconModule } from '@angular/material/icon';
|
|
||||||
import { MatPaginatorModule } from '@angular/material/paginator';
|
|
||||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
|
||||||
import { MatTableModule } from '@angular/material/table';
|
|
||||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
|
||||||
import { RouterModule } from '@angular/router';
|
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
|
||||||
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
|
|
||||||
import { AvatarModule } from 'src/app/modules/avatar/avatar.module';
|
|
||||||
|
|
||||||
import { MemberCreateDialogModule } from '../../../modules/add-member-dialog/member-create-dialog.module';
|
|
||||||
import { OrgContributorsComponent } from './org-contributors.component';
|
|
||||||
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
declarations: [OrgContributorsComponent],
|
|
||||||
imports: [
|
|
||||||
CommonModule,
|
|
||||||
FormsModule,
|
|
||||||
MemberCreateDialogModule,
|
|
||||||
HasRoleModule,
|
|
||||||
MatButtonModule,
|
|
||||||
MatDialogModule,
|
|
||||||
MatTableModule,
|
|
||||||
MatPaginatorModule,
|
|
||||||
MatIconModule,
|
|
||||||
RouterModule,
|
|
||||||
MatProgressSpinnerModule,
|
|
||||||
MatCheckboxModule,
|
|
||||||
MatTooltipModule,
|
|
||||||
TranslateModule,
|
|
||||||
AvatarModule,
|
|
||||||
],
|
|
||||||
exports: [
|
|
||||||
OrgContributorsComponent,
|
|
||||||
],
|
|
||||||
})
|
|
||||||
export class OrgContributorsModule { }
|
|
@@ -39,8 +39,11 @@
|
|||||||
|
|
||||||
<mat-tab-group mat-stretch-tabs class="tab-group" disablePagination="true">
|
<mat-tab-group mat-stretch-tabs class="tab-group" disablePagination="true">
|
||||||
<mat-tab label="Details">
|
<mat-tab label="Details">
|
||||||
<app-org-contributors *ngIf="org" [org]="org">
|
<app-contributors [totalResult]="totalMemberResult" [membersSubject]="membersSubject"
|
||||||
</app-org-contributors>
|
title="{{ 'PROJECT.MEMBER.TITLE' | translate }}"
|
||||||
|
description="{{ 'PROJECT.MEMBER.TITLEDESC' | translate }}" (addClicked)="openAddMember()"
|
||||||
|
(showDetailClicked)="showDetail()" [disabled]="false">
|
||||||
|
</app-contributors>
|
||||||
</mat-tab>
|
</mat-tab>
|
||||||
<mat-tab label="{{ 'CHANGES.ORG.TITLE' | translate }}" class="flex-col">
|
<mat-tab label="{{ 'CHANGES.ORG.TITLE' | translate }}" class="flex-col">
|
||||||
<app-changes *ngIf="org" [changeType]="ChangeType.ORG" [id]="org.id"></app-changes>
|
<app-changes *ngIf="org" [changeType]="ChangeType.ORG" [id]="org.id"></app-changes>
|
||||||
|
@@ -3,11 +3,22 @@ import { Component, OnDestroy, OnInit } from '@angular/core';
|
|||||||
import { MatButtonToggleChange } from '@angular/material/button-toggle';
|
import { MatButtonToggleChange } from '@angular/material/button-toggle';
|
||||||
import { MatDialog } from '@angular/material/dialog';
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
import { MatTableDataSource } from '@angular/material/table';
|
import { MatTableDataSource } from '@angular/material/table';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { Subscription } from 'rxjs';
|
import { BehaviorSubject, from, Observable, of, Subscription } from 'rxjs';
|
||||||
|
import { catchError, finalize, map } from 'rxjs/operators';
|
||||||
|
import { CreationType, MemberCreateDialogComponent } from 'src/app/modules/add-member-dialog/member-create-dialog.component';
|
||||||
import { ChangeType } from 'src/app/modules/changes/changes.component';
|
import { ChangeType } from 'src/app/modules/changes/changes.component';
|
||||||
import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component';
|
import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component';
|
||||||
import { Org, OrgDomainView, OrgMember, OrgMemberSearchResponse, OrgState } from 'src/app/proto/generated/management_pb';
|
import {
|
||||||
|
Org,
|
||||||
|
OrgDomainView,
|
||||||
|
OrgMember,
|
||||||
|
OrgMemberSearchResponse,
|
||||||
|
OrgMemberView,
|
||||||
|
OrgState,
|
||||||
|
User,
|
||||||
|
} from 'src/app/proto/generated/management_pb';
|
||||||
import { OrgService } from 'src/app/services/org.service';
|
import { OrgService } from 'src/app/services/org.service';
|
||||||
import { ToastService } from 'src/app/services/toast.service';
|
import { ToastService } from 'src/app/services/toast.service';
|
||||||
|
|
||||||
@@ -34,11 +45,19 @@ export class OrgDetailComponent implements OnInit, OnDestroy {
|
|||||||
public domains: OrgDomainView.AsObject[] = [];
|
public domains: OrgDomainView.AsObject[] = [];
|
||||||
public primaryDomain: string = '';
|
public primaryDomain: string = '';
|
||||||
|
|
||||||
|
// members
|
||||||
|
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||||
|
public loading$: Observable<boolean> = this.loadingSubject.asObservable();
|
||||||
|
public totalMemberResult: number = 0;
|
||||||
|
public membersSubject: BehaviorSubject<OrgMemberView.AsObject[]>
|
||||||
|
= new BehaviorSubject<OrgMemberView.AsObject[]>([]);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private dialog: MatDialog,
|
private dialog: MatDialog,
|
||||||
public translate: TranslateService,
|
public translate: TranslateService,
|
||||||
private orgService: OrgService,
|
private orgService: OrgService,
|
||||||
private toast: ToastService,
|
private toast: ToastService,
|
||||||
|
private router: Router,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
public ngOnInit(): void {
|
public ngOnInit(): void {
|
||||||
@@ -56,6 +75,18 @@ export class OrgDetailComponent implements OnInit, OnDestroy {
|
|||||||
this.toast.showError(error);
|
this.toast.showError(error);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.loadingSubject.next(true);
|
||||||
|
from(this.orgService.SearchMyOrgMembers(100, 0)).pipe(
|
||||||
|
map(resp => {
|
||||||
|
this.totalMemberResult = resp.toObject().totalResult;
|
||||||
|
return resp.toObject().resultList;
|
||||||
|
}),
|
||||||
|
catchError(() => of([])),
|
||||||
|
finalize(() => this.loadingSubject.next(false)),
|
||||||
|
).subscribe(members => {
|
||||||
|
this.membersSubject.next(members);
|
||||||
|
});
|
||||||
|
|
||||||
this.orgService.SearchMyOrgDomains(0, 100).then(result => {
|
this.orgService.SearchMyOrgDomains(0, 100).then(result => {
|
||||||
this.domains = result.toObject().resultList;
|
this.domains = result.toObject().resultList;
|
||||||
this.primaryDomain = this.domains.find(domain => domain.primary)?.domain ?? '';
|
this.primaryDomain = this.domains.find(domain => domain.primary)?.domain ?? '';
|
||||||
@@ -119,4 +150,36 @@ export class OrgDetailComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public openAddMember(): void {
|
||||||
|
const dialogRef = this.dialog.open(MemberCreateDialogComponent, {
|
||||||
|
data: {
|
||||||
|
creationType: CreationType.ORG,
|
||||||
|
},
|
||||||
|
width: '400px',
|
||||||
|
});
|
||||||
|
|
||||||
|
dialogRef.afterClosed().subscribe(resp => {
|
||||||
|
if (resp) {
|
||||||
|
const users: User.AsObject[] = resp.users;
|
||||||
|
const roles: string[] = resp.roles;
|
||||||
|
|
||||||
|
if (users && users.length && roles && roles.length) {
|
||||||
|
Promise.all(users.map(user => {
|
||||||
|
return this.orgService.AddMyOrgMember(user.id, roles);
|
||||||
|
})).then(() => {
|
||||||
|
this.toast.showInfo('ORG.TOAST.MEMBERADDED', true);
|
||||||
|
}).catch(error => {
|
||||||
|
this.toast.showError(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public showDetail(): void {
|
||||||
|
if (this.org?.state === OrgState.ORGSTATE_ACTIVE) {
|
||||||
|
this.router.navigate(['org/members']);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -12,13 +12,14 @@ import { MatTabsModule } from '@angular/material/tabs';
|
|||||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
|
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
|
||||||
|
import { MemberCreateDialogModule } from 'src/app/modules/add-member-dialog/member-create-dialog.module';
|
||||||
import { CardModule } from 'src/app/modules/card/card.module';
|
import { CardModule } from 'src/app/modules/card/card.module';
|
||||||
|
import { ContributorsModule } from 'src/app/modules/contributors/contributors.module';
|
||||||
import { MetaLayoutModule } from 'src/app/modules/meta-layout/meta-layout.module';
|
import { MetaLayoutModule } from 'src/app/modules/meta-layout/meta-layout.module';
|
||||||
import { SharedModule } from 'src/app/modules/shared/shared.module';
|
import { SharedModule } from 'src/app/modules/shared/shared.module';
|
||||||
import { WarnDialogModule } from 'src/app/modules/warn-dialog/warn-dialog.module';
|
import { WarnDialogModule } from 'src/app/modules/warn-dialog/warn-dialog.module';
|
||||||
|
|
||||||
import { ChangesModule } from '../../modules/changes/changes.module';
|
import { ChangesModule } from '../../modules/changes/changes.module';
|
||||||
import { OrgContributorsModule } from './org-contributors/org-contributors.module';
|
|
||||||
import { AddDomainDialogModule } from './org-detail/add-domain-dialog/add-domain-dialog.module';
|
import { AddDomainDialogModule } from './org-detail/add-domain-dialog/add-domain-dialog.module';
|
||||||
import { OrgDetailComponent } from './org-detail/org-detail.component';
|
import { OrgDetailComponent } from './org-detail/org-detail.component';
|
||||||
import { OrgGridComponent } from './org-grid/org-grid.component';
|
import { OrgGridComponent } from './org-grid/org-grid.component';
|
||||||
@@ -30,7 +31,6 @@ import { PolicyGridComponent } from './policy-grid/policy-grid.component';
|
|||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
OrgsRoutingModule,
|
OrgsRoutingModule,
|
||||||
OrgContributorsModule,
|
|
||||||
FormsModule,
|
FormsModule,
|
||||||
HasRoleModule,
|
HasRoleModule,
|
||||||
MatFormFieldModule,
|
MatFormFieldModule,
|
||||||
@@ -46,11 +46,13 @@ import { PolicyGridComponent } from './policy-grid/policy-grid.component';
|
|||||||
MatTooltipModule,
|
MatTooltipModule,
|
||||||
MatDialogModule,
|
MatDialogModule,
|
||||||
WarnDialogModule,
|
WarnDialogModule,
|
||||||
|
MemberCreateDialogModule,
|
||||||
MatMenuModule,
|
MatMenuModule,
|
||||||
ChangesModule,
|
ChangesModule,
|
||||||
AddDomainDialogModule,
|
AddDomainDialogModule,
|
||||||
TranslateModule,
|
TranslateModule,
|
||||||
SharedModule,
|
SharedModule,
|
||||||
|
ContributorsModule,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class OrgsModule { }
|
export class OrgsModule { }
|
||||||
|
@@ -3,84 +3,282 @@
|
|||||||
<button (click)="close()" mat-icon-button>
|
<button (click)="close()" mat-icon-button>
|
||||||
<mat-icon>close</mat-icon>
|
<mat-icon>close</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
<span class="abort">{{ 'APP.PAGES.CREATE_OIDC' | translate }}</span><span class="abort-2">Step
|
<span class="abort">{{ 'APP.PAGES.CREATE_OIDC' | translate }}</span>
|
||||||
{{ currentCreateStep }} of
|
|
||||||
{{ createSteps }}</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h1>{{'APP.PAGES.CREATE_OIDC_DESc' | translate}}</h1>
|
<h1>{{'APP.PAGES.CREATE_OIDC_DESC_TITLE' | translate}}</h1>
|
||||||
<form [formGroup]="form" (ngSubmit)="saveOIDCApp()">
|
<p class="desc">{{'APP.PAGES.CREATE_OIDC_DESC_SUB' | translate}}</p>
|
||||||
<div class="content">
|
|
||||||
<mat-form-field appearance="outline" class="formfield">
|
|
||||||
<mat-label>{{ 'APP.NAME' | translate }}</mat-label>
|
|
||||||
<input matInput formControlName="name" />
|
|
||||||
</mat-form-field>
|
|
||||||
|
|
||||||
<mat-form-field appearance="outline">
|
<mat-progress-bar color="accent" *ngIf="loading" mode="indeterminate"></mat-progress-bar>
|
||||||
<mat-label>{{ 'APP.OIDC.RESPONSE' | translate }}</mat-label>
|
|
||||||
<mat-select formControlName="responseTypesList" multiple>
|
|
||||||
<mat-option *ngFor="let type of oidcResponseTypes" [value]="type">
|
|
||||||
{{ 'APP.OIDC.RESPONSE'+type | translate }}
|
|
||||||
</mat-option>
|
|
||||||
</mat-select>
|
|
||||||
</mat-form-field>
|
|
||||||
|
|
||||||
<mat-form-field appearance="outline">
|
<mat-checkbox class="proswitch" color="primary" [(ngModel)]="devmode">
|
||||||
<mat-label>{{ 'APP.OIDC.GRANT' | translate }}</mat-label>
|
{{'APP.OIDC.PROSWITCH' | translate}}
|
||||||
<mat-select formControlName="grantTypesList" multiple>
|
</mat-checkbox>
|
||||||
<mat-option *ngFor="let grant of oidcGrantTypes" [value]="grant">
|
|
||||||
{{ 'APP.OIDC.GRANT'+grant | translate }}
|
|
||||||
</mat-option>
|
|
||||||
</mat-select>
|
|
||||||
</mat-form-field>
|
|
||||||
|
|
||||||
<mat-form-field appearance="outline" class="formfield">
|
<mat-horizontal-stepper *ngIf="!devmode" linear #stepper labelPosition="bottom">
|
||||||
<mat-label>{{ 'APP.OIDC.APPTYPE' | translate }}</mat-label>
|
<mat-step [stepControl]="firstFormGroup" [editable]="true">
|
||||||
<mat-select formControlName="applicationType">
|
<form [formGroup]="firstFormGroup">
|
||||||
<mat-option *ngFor="let type of oidcAppTypes" [value]="type">
|
<ng-template matStepLabel>{{'APP.OIDC.NAMEANDTYPESECTION' | translate}}</ng-template>
|
||||||
{{ 'APP.OIDC.APPTYPE'+type | translate }}
|
|
||||||
</mat-option>
|
|
||||||
</mat-select>
|
|
||||||
</mat-form-field>
|
|
||||||
|
|
||||||
<mat-form-field appearance="outline" class="formfield">
|
<p class="step-title">{{'APP.OIDC.TITLEFIRST' | translate}}</p>
|
||||||
<mat-label>{{ 'APP.OIDC.AUTHMETHOD' | translate }}</mat-label>
|
<mat-form-field appearance="outline" class="formfield">
|
||||||
<mat-select formControlName="authMethodType">
|
<mat-label>{{ 'APP.NAME' | translate }}</mat-label>
|
||||||
<mat-option *ngFor="let type of oidcAuthMethodType" [value]="type">
|
<input matInput formControlName="name" />
|
||||||
{{ 'APP.OIDC.AUTHMETHOD'+type | translate }}
|
<mat-error *ngIf="name?.errors?.required">{{'PROJECT.APP.NAMEREQUIRED' | translate}}</mat-error>
|
||||||
</mat-option>
|
</mat-form-field>
|
||||||
</mat-select>
|
|
||||||
</mat-form-field>
|
<p class="step-title">{{'APP.OIDC.TYPETITLE' | translate}}</p>
|
||||||
|
<mat-radio-group color="primary" aria-labelledby="example-radio-group-label" class="example-radio-group"
|
||||||
|
formControlName="applicationType">
|
||||||
|
<mat-radio-button *ngFor="let type of oidcAppTypes | keyvalue" [value]="type.value">
|
||||||
|
<div>{{'APP.OIDC.APPTYPE'+type.key | translate}}</div>
|
||||||
|
</mat-radio-button>
|
||||||
|
</mat-radio-group>
|
||||||
|
<div class="actions">
|
||||||
|
<button mat-raised-button [disabled]="firstFormGroup.invalid" color="primary"
|
||||||
|
matStepperNext>{{'ACTIONS.CONTINUE' | translate}}</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</mat-step>
|
||||||
|
|
||||||
|
<mat-step [editable]="true">
|
||||||
|
<ng-template matStepLabel>{{'APP.OIDC.GRANTSECTION' | translate}}</ng-template>
|
||||||
|
|
||||||
|
<p class="step-title">{{'APP.OIDC.GRANTTITLE' | translate}}</p>
|
||||||
|
|
||||||
|
<div class="checkbox-container">
|
||||||
|
<mat-checkbox (change)="changeGrant()" *ngFor="let granttype of oidcGrantTypes" color="primary"
|
||||||
|
[(ngModel)]="granttype.checked" [disabled]="granttype.disabled">
|
||||||
|
{{'APP.OIDC.GRANT'+granttype.type | translate}}
|
||||||
|
</mat-checkbox>
|
||||||
|
</div>
|
||||||
|
<div class="actions">
|
||||||
|
<button mat-stroked-button color="primary" matStepperPrevious>{{'ACTIONS.BACK' | translate}}</button>
|
||||||
|
<button mat-raised-button color="primary" matStepperNext>{{'ACTIONS.CONTINUE' | translate}}</button>
|
||||||
|
</div>
|
||||||
|
</mat-step>
|
||||||
|
|
||||||
|
<mat-step [editable]="true" *ngIf="!grantTypeChecked(OIDCGrantType.OIDCGRANTTYPE_AUTHORIZATION_CODE)">
|
||||||
|
<ng-template matStepLabel>{{'APP.OIDC.RESPONSESECTION' | translate}}</ng-template>
|
||||||
|
<div class="checkbox-container">
|
||||||
|
<mat-checkbox *ngFor="let responsetype of oidcResponseTypes | keyvalue" (change)="changeResponseType()"
|
||||||
|
color="primary" [(ngModel)]="responsetype.checked">
|
||||||
|
{{'APP.OIDC.RESPONSE'+responsetype.key | translate}}
|
||||||
|
</mat-checkbox>
|
||||||
|
</div>
|
||||||
|
<div class="actions">
|
||||||
|
<button mat-stroked-button color="primary" matStepperPrevious>{{'ACTIONS.BACK' | translate}}</button>
|
||||||
|
<button mat-raised-button color="primary" matStepperNext>{{'ACTIONS.CONTINUE' | translate}}</button>
|
||||||
|
</div>
|
||||||
|
</mat-step>
|
||||||
|
|
||||||
|
<mat-step [stepControl]="secondFormGroup" [editable]="true"
|
||||||
|
*ngIf="applicationType?.value !== OIDCApplicationType.OIDCAPPLICATIONTYPE_NATIVE">
|
||||||
|
<form [formGroup]="secondFormGroup">
|
||||||
|
<ng-template matStepLabel>{{'APP.OIDC.AUTHMETHODSECTION' | translate}}</ng-template>
|
||||||
|
|
||||||
|
<mat-radio-group color="primary" aria-labelledby="example-radio-group-label" class="example-radio-group"
|
||||||
|
formControlName="authMethodType">
|
||||||
|
<mat-radio-button *ngFor="let authmethod of oidcAuthMethodType" [disabled]="authmethod.disabled"
|
||||||
|
[value]="authmethod.type">
|
||||||
|
{{'APP.OIDC.AUTHMETHOD'+authmethod.type | translate}}
|
||||||
|
</mat-radio-button>
|
||||||
|
</mat-radio-group>
|
||||||
|
<div class="actions">
|
||||||
|
<button mat-stroked-button color="primary"
|
||||||
|
matStepperPrevious>{{'ACTIONS.BACK' | translate}}</button>
|
||||||
|
<button mat-raised-button color="primary" [disabled]="secondFormGroup.invalid"
|
||||||
|
matStepperNext>{{'ACTIONS.CONTINUE' | translate}}</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</mat-step>
|
||||||
|
|
||||||
|
<mat-step [editable]="true">
|
||||||
|
<ng-template matStepLabel>{{'APP.OIDC.REDIRECTSECTION' | translate}}</ng-template>
|
||||||
|
|
||||||
|
<p class="step-title">{{'APP.OIDC.REDIRECTTITLE' | translate}}</p>
|
||||||
|
|
||||||
<mat-form-field appearance="outline" class="full-width">
|
<mat-form-field appearance="outline" class="full-width">
|
||||||
<mat-label>{{ 'APP.OIDC.REDIRECT' | translate }}</mat-label>
|
<mat-label>{{ 'APP.OIDC.REDIRECT' | translate }}</mat-label>
|
||||||
<mat-chip-list #chipRedirectList aria-label="uri selection">
|
<mat-chip-list #chipRedirectList aria-label="uri selection">
|
||||||
<mat-chip *ngFor="let uri of oidcApp.redirectUrisList" removable
|
<mat-chip *ngFor="let uri of oidcApp.redirectUrisList" selected removable
|
||||||
(removed)="removeUri(uri, 'REDIRECT')">
|
[matTooltip]="!uri.startsWith('https://') ? ('APP.OIDC.UNSECUREREDIRECT' | translate): ''"
|
||||||
|
[color]="!uri.startsWith('https://') ? 'warn': 'white'" (removed)="removeUri(uri, 'REDIRECT')">
|
||||||
{{uri}} <mat-icon matChipRemove>cancel</mat-icon>
|
{{uri}} <mat-icon matChipRemove>cancel</mat-icon>
|
||||||
</mat-chip>
|
</mat-chip>
|
||||||
<input [matChipInputFor]="chipRedirectList" [matChipInputSeparatorKeyCodes]="separatorKeysCodes"
|
<input [matChipInputFor]="chipRedirectList" [matChipInputSeparatorKeyCodes]="separatorKeysCodes"
|
||||||
[matChipInputAddOnBlur]="addOnBlur" (matChipInputTokenEnd)="addUri($event, 'REDIRECT')">
|
[matChipInputAddOnBlur]="true" (matChipInputTokenEnd)="addUri($event, 'REDIRECT')">
|
||||||
</mat-chip-list>
|
</mat-chip-list>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|
||||||
|
<p class="step-title">{{'APP.OIDC.POSTREDIRECTTITLE' | translate}}</p>
|
||||||
|
|
||||||
<mat-form-field appearance="outline" class="full-width">
|
<mat-form-field appearance="outline" class="full-width">
|
||||||
<mat-label>{{ 'APP.OIDC.POSTLOGOUTREDIRECT' | translate }}</mat-label>
|
<mat-label>{{ 'APP.OIDC.POSTLOGOUTREDIRECT' | translate }}</mat-label>
|
||||||
<mat-chip-list #chipPostRedirectList aria-label="uri selection">
|
<mat-chip-list #chipPostRedirectList aria-label="uri selection">
|
||||||
<mat-chip *ngFor="let uri of oidcApp.postLogoutRedirectUrisList" removable
|
<mat-chip *ngFor="let uri of oidcApp.postLogoutRedirectUrisList"
|
||||||
(removed)="removeUri(uri, 'POSTREDIRECT')">
|
[matTooltip]="!uri.startsWith('https://') ? ('APP.OIDC.UNSECUREREDIRECT' | translate): ''"
|
||||||
|
removable (removed)="removeUri(uri, 'POSTREDIRECT')" selected
|
||||||
|
[color]="!uri.startsWith('https://') ? 'warn': 'white'">
|
||||||
{{uri}} <mat-icon matChipRemove>cancel</mat-icon>
|
{{uri}} <mat-icon matChipRemove>cancel</mat-icon>
|
||||||
</mat-chip>
|
</mat-chip>
|
||||||
<input [matChipInputFor]="chipPostRedirectList" [matChipInputSeparatorKeyCodes]="separatorKeysCodes"
|
<input [matChipInputFor]="chipPostRedirectList" [matChipInputSeparatorKeyCodes]="separatorKeysCodes"
|
||||||
[matChipInputAddOnBlur]="addOnBlur" (matChipInputTokenEnd)="addUri($event, 'POSTREDIRECT')">
|
[matChipInputAddOnBlur]="true" (matChipInputTokenEnd)="addUri($event, 'POSTREDIRECT')">
|
||||||
</mat-chip-list>
|
</mat-chip-list>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|
||||||
</div>
|
<div class="actions">
|
||||||
|
<button mat-stroked-button color="primary" matStepperPrevious>{{'ACTIONS.BACK' | translate}}</button>
|
||||||
|
<button mat-raised-button color="primary" matStepperNext>{{'ACTIONS.CONTINUE' | translate}}</button>
|
||||||
|
</div>
|
||||||
|
</mat-step>
|
||||||
|
|
||||||
<button color="primary" mat-raised-button class="continue-button" [disabled]="form.invalid" cdkFocusInitial
|
|
||||||
type="submit">
|
<mat-step>
|
||||||
{{ 'ACTIONS.SAVE' | translate }}
|
<ng-template matStepLabel>{{'APP.OIDC.OVERVIEWSECTION'| translate}}</ng-template>
|
||||||
</button>
|
<p class="step-title">{{'APP.OIDC.OVERVIEWTITLE' | translate}}</p>
|
||||||
</form>
|
<div class="row">
|
||||||
|
<span class="left">
|
||||||
|
{{ 'APP.NAME' | translate }}
|
||||||
|
</span>
|
||||||
|
<span class="right">
|
||||||
|
{{oidcApp.name}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<span class="left">
|
||||||
|
{{ 'APP.TYPE' | translate }}
|
||||||
|
</span>
|
||||||
|
<span class="right">
|
||||||
|
{{'APP.OIDC.APPTYPE'+oidcApp.applicationType | translate}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<span class="left">
|
||||||
|
{{ 'APP.GRANT' | translate }}
|
||||||
|
</span>
|
||||||
|
<span class="right">
|
||||||
|
<span *ngFor="let element of oidcApp.grantTypesList">
|
||||||
|
{{'APP.OIDC.GRANT'+element | translate}}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<span class="left">
|
||||||
|
{{ 'APP.OIDC.RESPONSE' | translate }}
|
||||||
|
</span>
|
||||||
|
<span class="right">
|
||||||
|
<span *ngFor="let element of oidcApp.responseTypesList">
|
||||||
|
{{'APP.OIDC.RESPONSE'+element | translate}}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<span class="left">
|
||||||
|
{{ 'APP.OIDC.REDIRECT' | translate }}
|
||||||
|
</span>
|
||||||
|
<span class="right">
|
||||||
|
<span *ngFor="let redirect of oidcApp.redirectUrisList">
|
||||||
|
{{redirect}}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<span class="left">
|
||||||
|
{{ 'APP.OIDC.POSTLOGOUTREDIRECT' | translate }}
|
||||||
|
</span>
|
||||||
|
<span class="right">
|
||||||
|
<span *ngFor="let redirect of oidcApp.postLogoutRedirectUrisList">
|
||||||
|
{{redirect}}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="actions">
|
||||||
|
<button mat-stroked-button color="primary" matStepperPrevious>{{'ACTIONS.BACK' | translate}}</button>
|
||||||
|
<button mat-raised-button color="primary"
|
||||||
|
(click)="saveOIDCApp()">{{'ACTIONS.CREATE' | translate}}</button>
|
||||||
|
</div>
|
||||||
|
</mat-step>
|
||||||
|
</mat-horizontal-stepper>
|
||||||
|
|
||||||
|
<div *ngIf="devmode" class="dev">
|
||||||
|
<form [formGroup]="form" (ngSubmit)="saveOIDCApp()">
|
||||||
|
<div class="content">
|
||||||
|
<mat-form-field appearance="outline" class="formfield">
|
||||||
|
<mat-label>{{ 'APP.NAME' | translate }}</mat-label>
|
||||||
|
<input matInput formControlName="name" />
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="formfield">
|
||||||
|
<mat-label>{{ 'APP.OIDC.APPTYPE' | translate }}</mat-label>
|
||||||
|
<mat-select formControlName="applicationType">
|
||||||
|
<mat-option *ngFor="let type of oidcAppTypes" [value]="type">
|
||||||
|
{{ 'APP.OIDC.APPTYPE'+type | translate }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>{{ 'APP.OIDC.GRANT' | translate }}</mat-label>
|
||||||
|
<mat-select formControlName="grantTypesList" multiple>
|
||||||
|
<mat-option *ngFor="let grant of oidcGrantTypes" [value]="grant.type">
|
||||||
|
{{ ('APP.OIDC.GRANT' + grant.type) | translate }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-label>{{ 'APP.OIDC.RESPONSE' | translate }}</mat-label>
|
||||||
|
<mat-select formControlName="responseTypesList" multiple>
|
||||||
|
<mat-option *ngFor="let type of oidcResponseTypes" [value]="type.type">
|
||||||
|
{{ 'APP.OIDC.RESPONSE'+type.type | translate }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="formfield">
|
||||||
|
<mat-label>{{ 'APP.OIDC.AUTHMETHOD' | translate }}</mat-label>
|
||||||
|
<mat-select formControlName="authMethodType">
|
||||||
|
<mat-option *ngFor="let type of oidcAuthMethodType" [value]="type.type">
|
||||||
|
{{ 'APP.OIDC.AUTHMETHOD'+type.type | translate }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="full-width">
|
||||||
|
<mat-label>{{ 'APP.OIDC.REDIRECT' | translate }}</mat-label>
|
||||||
|
<mat-chip-list #chipRedirectList aria-label="uri selection">
|
||||||
|
<mat-chip *ngFor="let uri of oidcApp.redirectUrisList" removable
|
||||||
|
(removed)="removeUri(uri, 'REDIRECT')">
|
||||||
|
{{uri}} <mat-icon matChipRemove>cancel</mat-icon>
|
||||||
|
</mat-chip>
|
||||||
|
<input [matChipInputFor]="chipRedirectList" [matChipInputSeparatorKeyCodes]="separatorKeysCodes"
|
||||||
|
[matChipInputAddOnBlur]="true" (matChipInputTokenEnd)="addUri($event, 'REDIRECT')">
|
||||||
|
</mat-chip-list>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" class="full-width">
|
||||||
|
<mat-label>{{ 'APP.OIDC.POSTLOGOUTREDIRECT' | translate }}</mat-label>
|
||||||
|
<mat-chip-list #chipPostRedirectList aria-label="uri selection">
|
||||||
|
<mat-chip *ngFor="let uri of oidcApp.postLogoutRedirectUrisList" removable
|
||||||
|
(removed)="removeUri(uri, 'POSTREDIRECT')">
|
||||||
|
{{uri}} <mat-icon matChipRemove>cancel</mat-icon>
|
||||||
|
</mat-chip>
|
||||||
|
<input [matChipInputFor]="chipPostRedirectList"
|
||||||
|
[matChipInputSeparatorKeyCodes]="separatorKeysCodes" [matChipInputAddOnBlur]="true"
|
||||||
|
(matChipInputTokenEnd)="addUri($event, 'POSTREDIRECT')">
|
||||||
|
</mat-chip-list>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button color="primary" mat-raised-button class="continue-button" [disabled]="form.invalid" cdkFocusInitial
|
||||||
|
type="submit">
|
||||||
|
{{ 'ACTIONS.SAVE' | translate }}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
@@ -1,5 +1,16 @@
|
|||||||
h1 {
|
h1 {
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
p.desc {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #8795a1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.proswitch {
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
@@ -26,50 +37,121 @@ h1 {
|
|||||||
margin-right: 0.5rem;
|
margin-right: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.column {
|
.formfield {
|
||||||
|
width: 400px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.full-width {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
mat-horizontal-stepper {
|
||||||
|
background: inherit !important;
|
||||||
|
margin: 0 -1.5rem;
|
||||||
|
|
||||||
|
.step-title {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
color: #8795a1;
|
||||||
|
}
|
||||||
|
|
||||||
|
mat-chip[color='white'] {
|
||||||
|
background-color: #fafafa;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mat-radio-group {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
.formfield {
|
mat-radio-button {
|
||||||
width: 400px;
|
margin: .5rem 0;
|
||||||
input {
|
|
||||||
font-size: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.autocomplete {
|
|
||||||
margin-top: 1rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.slider {
|
|
||||||
margin-top: 2rem;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
font-size: 0.8rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.access-token {
|
|
||||||
width: 400px;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
.checkbox-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
margin: 0 -.5rem;
|
flex-direction: column;
|
||||||
flex-wrap: wrap;
|
|
||||||
|
|
||||||
mat-form-field {
|
mat-checkbox {
|
||||||
flex: 1 0 40%;
|
margin: .5rem 0;
|
||||||
margin: 0 .5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.full-width {
|
|
||||||
flex-basis: 80%;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.continue-button {
|
.row {
|
||||||
margin-top: 3rem;
|
display: flex;
|
||||||
display: block;
|
justify-content: space-between;
|
||||||
padding: 0.5rem 4rem;
|
|
||||||
border-radius: 0.5rem;
|
.left, .right{
|
||||||
|
margin-bottom: .5rem;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #8795a1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
button[mat-stroked-button] {
|
||||||
|
float: left;
|
||||||
|
margin-top: 1rem;
|
||||||
|
border-radius: .5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
button[mat-raised-button] {
|
||||||
|
float: right;
|
||||||
|
margin-top: 1rem;
|
||||||
|
border-radius: .5rem;
|
||||||
|
padding: .5rem 1rem;
|
||||||
|
min-width: 100px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dev {
|
||||||
|
.column {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.formfield {
|
||||||
|
width: 400px;
|
||||||
|
input {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.autocomplete {
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider {
|
||||||
|
margin-top: 2rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.access-token {
|
||||||
|
width: 400px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
display: flex;
|
||||||
|
margin: 0 -.5rem;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
mat-form-field {
|
||||||
|
flex: 1 0 40%;
|
||||||
|
margin: 0 .5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.full-width {
|
||||||
|
flex-basis: 80%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.continue-button {
|
||||||
|
margin-top: 3rem;
|
||||||
|
display: block;
|
||||||
|
padding: 0.5rem 4rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -6,6 +6,7 @@ import { MatChipInputEvent } from '@angular/material/chips';
|
|||||||
import { MatDialog } from '@angular/material/dialog';
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
import { ActivatedRoute, Params, Router } from '@angular/router';
|
import { ActivatedRoute, Params, Router } from '@angular/router';
|
||||||
import { Subscription } from 'rxjs';
|
import { Subscription } from 'rxjs';
|
||||||
|
import { debounceTime } from 'rxjs/operators';
|
||||||
import {
|
import {
|
||||||
Application,
|
Application,
|
||||||
OIDCApplicationCreate,
|
OIDCApplicationCreate,
|
||||||
@@ -26,35 +27,49 @@ import { AppSecretDialogComponent } from '../app-secret-dialog/app-secret-dialog
|
|||||||
})
|
})
|
||||||
export class AppCreateComponent implements OnInit, OnDestroy {
|
export class AppCreateComponent implements OnInit, OnDestroy {
|
||||||
private subscription?: Subscription;
|
private subscription?: Subscription;
|
||||||
|
public devmode: boolean = false;
|
||||||
public projectId: string = '';
|
public projectId: string = '';
|
||||||
|
public loading: boolean = false;
|
||||||
public oidcApp: OIDCApplicationCreate.AsObject = new OIDCApplicationCreate().toObject();
|
public oidcApp: OIDCApplicationCreate.AsObject = new OIDCApplicationCreate().toObject();
|
||||||
public oidcResponseTypes: OIDCResponseType[] = [
|
|
||||||
OIDCResponseType.OIDCRESPONSETYPE_CODE,
|
public oidcResponseTypes: { type: OIDCResponseType, checked: boolean; }[] = [
|
||||||
OIDCResponseType.OIDCRESPONSETYPE_ID_TOKEN,
|
{ type: OIDCResponseType.OIDCRESPONSETYPE_CODE, checked: false },
|
||||||
OIDCResponseType.OIDCRESPONSETYPE_ID_TOKEN_TOKEN,
|
{ type: OIDCResponseType.OIDCRESPONSETYPE_ID_TOKEN, checked: false },
|
||||||
];
|
{ type: OIDCResponseType.OIDCRESPONSETYPE_ID_TOKEN_TOKEN, checked: false },
|
||||||
public oidcGrantTypes: OIDCGrantType[] = [
|
|
||||||
OIDCGrantType.OIDCGRANTTYPE_AUTHORIZATION_CODE,
|
|
||||||
OIDCGrantType.OIDCGRANTTYPE_IMPLICIT,
|
|
||||||
OIDCGrantType.OIDCGRANTTYPE_REFRESH_TOKEN,
|
|
||||||
];
|
];
|
||||||
|
public oidcGrantTypes: {
|
||||||
|
type: OIDCGrantType,
|
||||||
|
checked: boolean,
|
||||||
|
disabled: boolean,
|
||||||
|
}[] = [
|
||||||
|
{ type: OIDCGrantType.OIDCGRANTTYPE_AUTHORIZATION_CODE, checked: true, disabled: false },
|
||||||
|
{ type: OIDCGrantType.OIDCGRANTTYPE_IMPLICIT, checked: false, disabled: true },
|
||||||
|
// { type: OIDCGrantType.OIDCGRANTTYPE_REFRESH_TOKEN, checked: false, disabled: true },
|
||||||
|
// TODO show when implemented
|
||||||
|
];
|
||||||
public oidcAppTypes: OIDCApplicationType[] = [
|
public oidcAppTypes: OIDCApplicationType[] = [
|
||||||
OIDCApplicationType.OIDCAPPLICATIONTYPE_WEB,
|
OIDCApplicationType.OIDCAPPLICATIONTYPE_WEB,
|
||||||
OIDCApplicationType.OIDCAPPLICATIONTYPE_USER_AGENT,
|
OIDCApplicationType.OIDCAPPLICATIONTYPE_USER_AGENT,
|
||||||
OIDCApplicationType.OIDCAPPLICATIONTYPE_NATIVE,
|
OIDCApplicationType.OIDCAPPLICATIONTYPE_NATIVE,
|
||||||
];
|
];
|
||||||
public oidcAuthMethodType: OIDCAuthMethodType[] = [
|
public oidcAuthMethodType: { type: OIDCAuthMethodType, checked: boolean, disabled: boolean; }[] = [
|
||||||
OIDCAuthMethodType.OIDCAUTHMETHODTYPE_BASIC,
|
{ type: OIDCAuthMethodType.OIDCAUTHMETHODTYPE_BASIC, checked: false, disabled: false },
|
||||||
OIDCAuthMethodType.OIDCAUTHMETHODTYPE_NONE,
|
{ type: OIDCAuthMethodType.OIDCAUTHMETHODTYPE_NONE, checked: false, disabled: false },
|
||||||
OIDCAuthMethodType.OIDCAUTHMETHODTYPE_POST,
|
{ type: OIDCAuthMethodType.OIDCAUTHMETHODTYPE_POST, checked: false, disabled: false },
|
||||||
];
|
];
|
||||||
|
|
||||||
public form!: FormGroup;
|
// stepper
|
||||||
public createSteps: number = 1;
|
firstFormGroup!: FormGroup;
|
||||||
public currentCreateStep: number = 1;
|
secondFormGroup!: FormGroup;
|
||||||
public postLogoutRedirectUrisList: string[] = [];
|
thirdFormGroup!: FormGroup;
|
||||||
|
|
||||||
|
// devmode
|
||||||
|
public form!: FormGroup;
|
||||||
|
|
||||||
|
public OIDCApplicationType: any = OIDCApplicationType;
|
||||||
|
public OIDCGrantType: any = OIDCGrantType;
|
||||||
|
public OIDCAuthMethodType: any = OIDCAuthMethodType;
|
||||||
|
|
||||||
public addOnBlur: boolean = true;
|
|
||||||
public readonly separatorKeysCodes: number[] = [ENTER, COMMA, SPACE];
|
public readonly separatorKeysCodes: number[] = [ENTER, COMMA, SPACE];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@@ -68,10 +83,68 @@ export class AppCreateComponent implements OnInit, OnDestroy {
|
|||||||
) {
|
) {
|
||||||
this.form = this.fb.group({
|
this.form = this.fb.group({
|
||||||
name: ['', [Validators.required]],
|
name: ['', [Validators.required]],
|
||||||
responseTypesList: ['', []],
|
responseTypesList: ['', [Validators.required]],
|
||||||
grantTypesList: ['', []],
|
grantTypesList: ['', [Validators.required]],
|
||||||
applicationType: ['', []],
|
applicationType: ['', [Validators.required]],
|
||||||
authMethodType: [],
|
authMethodType: ['', [Validators.required]],
|
||||||
|
});
|
||||||
|
|
||||||
|
this.form.valueChanges.pipe(debounceTime(300)).subscribe((value) => {
|
||||||
|
this.oidcApp.name = this.formname?.value;
|
||||||
|
this.oidcApp.applicationType = this.formapplicationType?.value;
|
||||||
|
this.oidcApp.grantTypesList = this.formgrantTypesList?.value;
|
||||||
|
this.oidcApp.responseTypesList = this.formresponseTypesList?.value;
|
||||||
|
this.oidcApp.authMethodType = this.formauthMethodType?.value;
|
||||||
|
|
||||||
|
console.log(this.oidcApp);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.firstFormGroup = this.fb.group({
|
||||||
|
name: ['', [Validators.required]],
|
||||||
|
applicationType: ['', [Validators.required]],
|
||||||
|
});
|
||||||
|
this.secondFormGroup = this.fb.group({
|
||||||
|
authMethodType: [OIDCAuthMethodType.OIDCAUTHMETHODTYPE_BASIC, [Validators.required]],
|
||||||
|
});
|
||||||
|
|
||||||
|
this.oidcApp.authMethodType = OIDCAuthMethodType.OIDCAUTHMETHODTYPE_BASIC;
|
||||||
|
|
||||||
|
this.firstFormGroup.valueChanges.subscribe(value => {
|
||||||
|
if (this.applicationType?.value === OIDCApplicationType.OIDCAPPLICATIONTYPE_NATIVE) {
|
||||||
|
this.oidcResponseTypes[0].checked = true;
|
||||||
|
// this.oidcApp.responseTypesList = [this.oidcResponseTypes[0].type];
|
||||||
|
|
||||||
|
this.authMethodType?.setValue(OIDCAuthMethodType.OIDCAUTHMETHODTYPE_NONE);
|
||||||
|
this.oidcAuthMethodType[1].disabled = true;
|
||||||
|
}
|
||||||
|
if (this.applicationType?.value === OIDCApplicationType.OIDCAPPLICATIONTYPE_WEB) {
|
||||||
|
this.oidcResponseTypes[0].checked = true;
|
||||||
|
this.oidcApp.responseTypesList = [this.oidcResponseTypes[0].type];
|
||||||
|
|
||||||
|
this.authMethodType?.setValue(OIDCAuthMethodType.OIDCAUTHMETHODTYPE_NONE);
|
||||||
|
this.oidcAuthMethodType[0].disabled = true;
|
||||||
|
this.oidcAuthMethodType[2].disabled = true;
|
||||||
|
}
|
||||||
|
if (this.applicationType?.value === OIDCApplicationType.OIDCAPPLICATIONTYPE_USER_AGENT) {
|
||||||
|
this.oidcResponseTypes[0].checked = true;
|
||||||
|
this.oidcApp.responseTypesList = [this.oidcResponseTypes[0].type];
|
||||||
|
|
||||||
|
this.authMethodType?.setValue(OIDCAuthMethodType.OIDCAUTHMETHODTYPE_NONE);
|
||||||
|
this.oidcAuthMethodType[0].disabled = true;
|
||||||
|
this.oidcAuthMethodType[2].disabled = true;
|
||||||
|
|
||||||
|
this.oidcGrantTypes[1].disabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.changeResponseType();
|
||||||
|
this.changeGrant();
|
||||||
|
|
||||||
|
this.oidcApp.name = this.name?.value;
|
||||||
|
this.oidcApp.applicationType = this.applicationType?.value;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.secondFormGroup.valueChanges.subscribe(value => {
|
||||||
|
this.oidcApp.authMethodType = this.authMethodType?.value;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,18 +166,15 @@ export class AppCreateComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public saveOIDCApp(): void {
|
public saveOIDCApp(): void {
|
||||||
this.oidcApp.name = this.name?.value;
|
this.loading = true;
|
||||||
this.oidcApp.applicationType = this.applicationType?.value;
|
|
||||||
this.oidcApp.grantTypesList = this.grantTypesList?.value;
|
|
||||||
this.oidcApp.responseTypesList = this.responseTypesList?.value;
|
|
||||||
this.oidcApp.authMethodType = this.authMethodType?.value;
|
|
||||||
|
|
||||||
this.projectService
|
this.projectService
|
||||||
.CreateOIDCApp(this.oidcApp)
|
.CreateOIDCApp(this.oidcApp)
|
||||||
.then((data: Application) => {
|
.then((data: Application) => {
|
||||||
|
this.loading = false;
|
||||||
this.showSavedDialog(data.toObject());
|
this.showSavedDialog(data.toObject());
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
|
this.loading = false;
|
||||||
this.toast.showError(error);
|
this.toast.showError(error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -154,29 +224,58 @@ export class AppCreateComponent implements OnInit, OnDestroy {
|
|||||||
this.oidcApp.postLogoutRedirectUrisList.splice(index, 1);
|
this.oidcApp.postLogoutRedirectUrisList.splice(index, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
changeGrant(): void {
|
||||||
|
this.oidcApp.grantTypesList = this.oidcGrantTypes.filter(gt => gt.checked).map(gt => gt.type);
|
||||||
|
}
|
||||||
|
|
||||||
|
changeResponseType(): void {
|
||||||
|
this.oidcApp.responseTypesList = this.oidcResponseTypes.filter(gt => gt.checked).map(gt => gt.type);
|
||||||
}
|
}
|
||||||
|
|
||||||
get name(): AbstractControl | null {
|
get name(): AbstractControl | null {
|
||||||
|
return this.firstFormGroup.get('name');
|
||||||
|
}
|
||||||
|
|
||||||
|
get applicationType(): AbstractControl | null {
|
||||||
|
return this.firstFormGroup.get('applicationType');
|
||||||
|
}
|
||||||
|
|
||||||
|
public grantTypeChecked(type: OIDCGrantType): boolean {
|
||||||
|
return this.oidcGrantTypes.filter(gt => gt.checked).map(gt => gt.type).findIndex(t => t === type) > -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
get responseTypesList(): AbstractControl | null {
|
||||||
|
return this.secondFormGroup.get('responseTypesList');
|
||||||
|
}
|
||||||
|
|
||||||
|
get authMethodType(): AbstractControl | null {
|
||||||
|
return this.secondFormGroup.get('authMethodType');
|
||||||
|
}
|
||||||
|
|
||||||
|
// devmode
|
||||||
|
|
||||||
|
get formname(): AbstractControl | null {
|
||||||
return this.form.get('name');
|
return this.form.get('name');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
get responseTypesList(): AbstractControl | null {
|
get formresponseTypesList(): AbstractControl | null {
|
||||||
return this.form.get('responseTypesList');
|
return this.form.get('responseTypesList');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
get grantTypesList(): AbstractControl | null {
|
get formgrantTypesList(): AbstractControl | null {
|
||||||
return this.form.get('grantTypesList');
|
return this.form.get('grantTypesList');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
get applicationType(): AbstractControl | null {
|
get formapplicationType(): AbstractControl | null {
|
||||||
return this.form.get('applicationType');
|
return this.form.get('applicationType');
|
||||||
}
|
}
|
||||||
|
|
||||||
get authMethodType(): AbstractControl | null {
|
get formauthMethodType(): AbstractControl | null {
|
||||||
return this.form.get('authMethodType');
|
return this.form.get('authMethodType');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -12,7 +12,9 @@ import { MatInputModule } from '@angular/material/input';
|
|||||||
import { MatMenuModule } from '@angular/material/menu';
|
import { MatMenuModule } from '@angular/material/menu';
|
||||||
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
||||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||||
|
import { MatRadioModule } from '@angular/material/radio';
|
||||||
import { MatSelectModule } from '@angular/material/select';
|
import { MatSelectModule } from '@angular/material/select';
|
||||||
|
import { MatStepperModule } from '@angular/material/stepper';
|
||||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
|
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
|
||||||
@@ -51,6 +53,8 @@ import { AppsRoutingModule } from './apps-routing.module';
|
|||||||
CardModule,
|
CardModule,
|
||||||
MatTooltipModule,
|
MatTooltipModule,
|
||||||
TranslateModule,
|
TranslateModule,
|
||||||
|
MatStepperModule,
|
||||||
|
MatRadioModule,
|
||||||
],
|
],
|
||||||
exports: [TranslateModule],
|
exports: [TranslateModule],
|
||||||
})
|
})
|
||||||
|
@@ -34,10 +34,11 @@
|
|||||||
|
|
||||||
<mat-tab-group mat-stretch-tabs class="tab-group" disablePagination="true">
|
<mat-tab-group mat-stretch-tabs class="tab-group" disablePagination="true">
|
||||||
<mat-tab label="Details">
|
<mat-tab label="Details">
|
||||||
<app-project-contributors *ngIf="project && grantId"
|
<app-contributors [totalResult]="totalMemberResult" [membersSubject]="membersSubject"
|
||||||
[disabled]="project?.state !== ProjectState.PROJECTSTATE_ACTIVE"
|
title="{{ 'PROJECT.MEMBER.TITLE' | translate }}"
|
||||||
[projectType]="ProjectType.PROJECTTYPE_GRANTED" [project]="project" [grantId]="grantId">
|
description="{{ 'PROJECT.MEMBER.TITLEDESC' | translate }}" (addClicked)="openAddMember()"
|
||||||
</app-project-contributors>
|
(showDetailClicked)="showDetail()" [disabled]="false">
|
||||||
|
</app-contributors>
|
||||||
</mat-tab>
|
</mat-tab>
|
||||||
<mat-tab label="{{ 'CHANGES.PROJECT.TITLE' | translate }}" class="flex-col">
|
<mat-tab label="{{ 'CHANGES.PROJECT.TITLE' | translate }}" class="flex-col">
|
||||||
<app-changes *ngIf="project" [changeType]="ChangeType.PROJECT" [id]="project.id"></app-changes>
|
<app-changes *ngIf="project" [changeType]="ChangeType.PROJECT" [id]="project.id"></app-changes>
|
||||||
|
@@ -1,22 +1,28 @@
|
|||||||
import { SelectionModel } from '@angular/cdk/collections';
|
import { SelectionModel } from '@angular/cdk/collections';
|
||||||
import { Location } from '@angular/common';
|
import { Location } from '@angular/common';
|
||||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||||
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
import { MatTableDataSource } from '@angular/material/table';
|
import { MatTableDataSource } from '@angular/material/table';
|
||||||
import { ActivatedRoute, Params } from '@angular/router';
|
import { ActivatedRoute, Params, Router } from '@angular/router';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { Subscription } from 'rxjs';
|
import { BehaviorSubject, from, of, Subscription } from 'rxjs';
|
||||||
|
import { catchError, finalize, map } from 'rxjs/operators';
|
||||||
|
import { CreationType, MemberCreateDialogComponent } from 'src/app/modules/add-member-dialog/member-create-dialog.component';
|
||||||
import { ChangeType } from 'src/app/modules/changes/changes.component';
|
import { ChangeType } from 'src/app/modules/changes/changes.component';
|
||||||
import { UserGrantContext } from 'src/app/modules/user-grants/user-grants-datasource';
|
import { UserGrantContext } from 'src/app/modules/user-grants/user-grants-datasource';
|
||||||
import {
|
import {
|
||||||
Application,
|
Application,
|
||||||
ApplicationSearchResponse,
|
ApplicationSearchResponse,
|
||||||
|
ProjectGrantState,
|
||||||
ProjectGrantView,
|
ProjectGrantView,
|
||||||
ProjectMember,
|
ProjectMember,
|
||||||
ProjectMemberSearchResponse,
|
ProjectMemberSearchResponse,
|
||||||
|
ProjectMemberView,
|
||||||
ProjectRole,
|
ProjectRole,
|
||||||
ProjectRoleSearchResponse,
|
ProjectRoleSearchResponse,
|
||||||
ProjectState,
|
ProjectState,
|
||||||
ProjectType,
|
ProjectType,
|
||||||
|
User,
|
||||||
UserGrantSearchKey,
|
UserGrantSearchKey,
|
||||||
} from 'src/app/proto/generated/management_pb';
|
} from 'src/app/proto/generated/management_pb';
|
||||||
import { OrgService } from 'src/app/services/org.service';
|
import { OrgService } from 'src/app/services/org.service';
|
||||||
@@ -62,6 +68,12 @@ export class GrantedProjectDetailComponent implements OnInit, OnDestroy {
|
|||||||
public userGrantContext: UserGrantContext = UserGrantContext.GRANTED_PROJECT;
|
public userGrantContext: UserGrantContext = UserGrantContext.GRANTED_PROJECT;
|
||||||
public userGrantSearchKey: UserGrantSearchKey = UserGrantSearchKey.USERGRANTSEARCHKEY_PROJECT_ID;
|
public userGrantSearchKey: UserGrantSearchKey = UserGrantSearchKey.USERGRANTSEARCHKEY_PROJECT_ID;
|
||||||
|
|
||||||
|
// members
|
||||||
|
public totalMemberResult: number = 0;
|
||||||
|
public membersSubject: BehaviorSubject<ProjectMemberView.AsObject[]>
|
||||||
|
= new BehaviorSubject<ProjectMemberView.AsObject[]>([]);
|
||||||
|
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public translate: TranslateService,
|
public translate: TranslateService,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
@@ -69,6 +81,8 @@ export class GrantedProjectDetailComponent implements OnInit, OnDestroy {
|
|||||||
private projectService: ProjectService,
|
private projectService: ProjectService,
|
||||||
private _location: Location,
|
private _location: Location,
|
||||||
private orgService: OrgService,
|
private orgService: OrgService,
|
||||||
|
private router: Router,
|
||||||
|
private dialog: MatDialog,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,10 +108,60 @@ export class GrantedProjectDetailComponent implements OnInit, OnDestroy {
|
|||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
this.toast.showError(error);
|
this.toast.showError(error);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
from(this.projectService.SearchProjectGrantMembers(this.projectId,
|
||||||
|
this.projectId, 100, 0)).pipe(
|
||||||
|
map(resp => {
|
||||||
|
this.totalMemberResult = resp.toObject().totalResult;
|
||||||
|
return resp.toObject().resultList;
|
||||||
|
}),
|
||||||
|
catchError(() => of([])),
|
||||||
|
finalize(() => this.loadingSubject.next(false)),
|
||||||
|
).subscribe(members => {
|
||||||
|
this.membersSubject.next(members);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public navigateBack(): void {
|
public navigateBack(): void {
|
||||||
this._location.back();
|
this._location.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public openAddMember(): void {
|
||||||
|
const dialogRef = this.dialog.open(MemberCreateDialogComponent, {
|
||||||
|
data: {
|
||||||
|
creationType: CreationType.PROJECT_GRANTED,
|
||||||
|
projectId: this.project.projectId,
|
||||||
|
},
|
||||||
|
width: '400px',
|
||||||
|
});
|
||||||
|
|
||||||
|
dialogRef.afterClosed().subscribe(resp => {
|
||||||
|
if (resp) {
|
||||||
|
const users: User.AsObject[] = resp.users;
|
||||||
|
const roles: string[] = resp.roles;
|
||||||
|
|
||||||
|
if (users && users.length && roles && roles.length) {
|
||||||
|
users.forEach(user => {
|
||||||
|
return this.projectService.AddProjectGrantMember(
|
||||||
|
this.projectId,
|
||||||
|
this.grantId,
|
||||||
|
user.id,
|
||||||
|
roles,
|
||||||
|
).then(() => {
|
||||||
|
this.toast.showInfo('PROJECT.TOAST.MEMBERADDED', true);
|
||||||
|
}).catch(error => {
|
||||||
|
this.toast.showError(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public showDetail(): void {
|
||||||
|
if (this.project.state === ProjectGrantState.PROJECTGRANTSTATE_ACTIVE) {
|
||||||
|
this.router.navigate(['granted-projects', this.project.projectId, 'grant', this.grantId, 'members']);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -16,10 +16,11 @@ import { MatTabsModule } from '@angular/material/tabs';
|
|||||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
|
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
|
||||||
|
import { MemberCreateDialogModule } from 'src/app/modules/add-member-dialog/member-create-dialog.module';
|
||||||
import { CardModule } from 'src/app/modules/card/card.module';
|
import { CardModule } from 'src/app/modules/card/card.module';
|
||||||
import { ChangesModule } from 'src/app/modules/changes/changes.module';
|
import { ChangesModule } from 'src/app/modules/changes/changes.module';
|
||||||
|
import { ContributorsModule } from 'src/app/modules/contributors/contributors.module';
|
||||||
import { MetaLayoutModule } from 'src/app/modules/meta-layout/meta-layout.module';
|
import { MetaLayoutModule } from 'src/app/modules/meta-layout/meta-layout.module';
|
||||||
import { ProjectContributorsModule } from 'src/app/modules/project-contributors/project-contributors.module';
|
|
||||||
import { ProjectRolesModule } from 'src/app/modules/project-roles/project-roles.module';
|
import { ProjectRolesModule } from 'src/app/modules/project-roles/project-roles.module';
|
||||||
import { SharedModule } from 'src/app/modules/shared/shared.module';
|
import { SharedModule } from 'src/app/modules/shared/shared.module';
|
||||||
import { UserGrantsModule } from 'src/app/modules/user-grants/user-grants.module';
|
import { UserGrantsModule } from 'src/app/modules/user-grants/user-grants.module';
|
||||||
@@ -43,7 +44,7 @@ import { GrantedProjectsComponent } from './granted-projects.component';
|
|||||||
CommonModule,
|
CommonModule,
|
||||||
UserGrantsModule,
|
UserGrantsModule,
|
||||||
GrantedProjectsRoutingModule,
|
GrantedProjectsRoutingModule,
|
||||||
ProjectContributorsModule,
|
ContributorsModule,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
TranslateModule,
|
TranslateModule,
|
||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
@@ -69,6 +70,7 @@ import { GrantedProjectsComponent } from './granted-projects.component';
|
|||||||
TranslateModule,
|
TranslateModule,
|
||||||
TimestampToDatePipeModule,
|
TimestampToDatePipeModule,
|
||||||
SharedModule,
|
SharedModule,
|
||||||
|
MemberCreateDialogModule,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class GrantedProjectsModule { }
|
export class GrantedProjectsModule { }
|
||||||
|
@@ -101,10 +101,11 @@
|
|||||||
|
|
||||||
<mat-tab-group mat-stretch-tabs class="tab-group" disablePagination="true">
|
<mat-tab-group mat-stretch-tabs class="tab-group" disablePagination="true">
|
||||||
<mat-tab label="Details">
|
<mat-tab label="Details">
|
||||||
<app-project-contributors *ngIf="project"
|
<app-contributors [totalResult]="totalMemberResult" [membersSubject]="membersSubject"
|
||||||
[disabled]="project?.state !== ProjectState.PROJECTSTATE_ACTIVE"
|
title="{{ 'PROJECT.MEMBER.TITLE' | translate }}"
|
||||||
[projectType]="ProjectType.PROJECTTYPE_OWNED" [project]="project">
|
description="{{ 'PROJECT.MEMBER.TITLEDESC' | translate }}" (addClicked)="openAddMember()"
|
||||||
</app-project-contributors>
|
(showDetailClicked)="showDetail()" [disabled]="false">
|
||||||
|
</app-contributors>
|
||||||
</mat-tab>
|
</mat-tab>
|
||||||
<mat-tab label="{{ 'CHANGES.PROJECT.TITLE' | translate }}" class="flex-col">
|
<mat-tab label="{{ 'CHANGES.PROJECT.TITLE' | translate }}" class="flex-col">
|
||||||
<app-changes *ngIf="project" [changeType]="ChangeType.PROJECT" [id]="project.projectId"></app-changes>
|
<app-changes *ngIf="project" [changeType]="ChangeType.PROJECT" [id]="project.projectId"></app-changes>
|
||||||
|
@@ -3,9 +3,11 @@ import { Location } from '@angular/common';
|
|||||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||||
import { MatDialog } from '@angular/material/dialog';
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
import { MatTableDataSource } from '@angular/material/table';
|
import { MatTableDataSource } from '@angular/material/table';
|
||||||
import { ActivatedRoute, Params } from '@angular/router';
|
import { ActivatedRoute, Params, Router } from '@angular/router';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { Subscription } from 'rxjs';
|
import { BehaviorSubject, from, of, Subscription } from 'rxjs';
|
||||||
|
import { catchError, finalize, map } from 'rxjs/operators';
|
||||||
|
import { CreationType, MemberCreateDialogComponent } from 'src/app/modules/add-member-dialog/member-create-dialog.component';
|
||||||
import { ChangeType } from 'src/app/modules/changes/changes.component';
|
import { ChangeType } from 'src/app/modules/changes/changes.component';
|
||||||
import { UserGrantContext } from 'src/app/modules/user-grants/user-grants-datasource';
|
import { UserGrantContext } from 'src/app/modules/user-grants/user-grants-datasource';
|
||||||
import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component';
|
import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component';
|
||||||
@@ -14,11 +16,13 @@ import {
|
|||||||
ApplicationSearchResponse,
|
ApplicationSearchResponse,
|
||||||
ProjectMember,
|
ProjectMember,
|
||||||
ProjectMemberSearchResponse,
|
ProjectMemberSearchResponse,
|
||||||
|
ProjectMemberView,
|
||||||
ProjectRole,
|
ProjectRole,
|
||||||
ProjectRoleSearchResponse,
|
ProjectRoleSearchResponse,
|
||||||
ProjectState,
|
ProjectState,
|
||||||
ProjectType,
|
ProjectType,
|
||||||
ProjectView,
|
ProjectView,
|
||||||
|
User,
|
||||||
UserGrantSearchKey,
|
UserGrantSearchKey,
|
||||||
} from 'src/app/proto/generated/management_pb';
|
} from 'src/app/proto/generated/management_pb';
|
||||||
import { OrgService } from 'src/app/services/org.service';
|
import { OrgService } from 'src/app/services/org.service';
|
||||||
@@ -64,6 +68,12 @@ export class OwnedProjectDetailComponent implements OnInit, OnDestroy {
|
|||||||
public userGrantSearchKey: UserGrantSearchKey = UserGrantSearchKey.USERGRANTSEARCHKEY_PROJECT_ID;
|
public userGrantSearchKey: UserGrantSearchKey = UserGrantSearchKey.USERGRANTSEARCHKEY_PROJECT_ID;
|
||||||
public userGrantContext: UserGrantContext = UserGrantContext.OWNED_PROJECT;
|
public userGrantContext: UserGrantContext = UserGrantContext.OWNED_PROJECT;
|
||||||
|
|
||||||
|
// members
|
||||||
|
public totalMemberResult: number = 0;
|
||||||
|
public membersSubject: BehaviorSubject<ProjectMemberView.AsObject[]>
|
||||||
|
= new BehaviorSubject<ProjectMemberView.AsObject[]>([]);
|
||||||
|
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public translate: TranslateService,
|
public translate: TranslateService,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
@@ -72,6 +82,7 @@ export class OwnedProjectDetailComponent implements OnInit, OnDestroy {
|
|||||||
private _location: Location,
|
private _location: Location,
|
||||||
private orgService: OrgService,
|
private orgService: OrgService,
|
||||||
private dialog: MatDialog,
|
private dialog: MatDialog,
|
||||||
|
private router: Router,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,14 +101,23 @@ export class OwnedProjectDetailComponent implements OnInit, OnDestroy {
|
|||||||
this.isZitadel = iam.toObject().iamProjectId === this.projectId;
|
this.isZitadel = iam.toObject().iamProjectId === this.projectId;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this.projectId) {
|
this.projectService.GetProjectById(id).then(proj => {
|
||||||
this.projectService.GetProjectById(id).then(proj => {
|
this.project = proj.toObject();
|
||||||
this.project = proj.toObject();
|
}).catch(error => {
|
||||||
}).catch(error => {
|
console.error(error);
|
||||||
console.error(error);
|
this.toast.showError(error);
|
||||||
this.toast.showError(error);
|
});
|
||||||
});
|
|
||||||
}
|
from(this.projectService.SearchProjectMembers(this.projectId, 100, 0)).pipe(
|
||||||
|
map(resp => {
|
||||||
|
this.totalMemberResult = resp.toObject().totalResult;
|
||||||
|
return resp.toObject().resultList;
|
||||||
|
}),
|
||||||
|
catchError(() => of([])),
|
||||||
|
finalize(() => this.loadingSubject.next(false)),
|
||||||
|
).subscribe(members => {
|
||||||
|
this.membersSubject.next(members);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public changeState(newState: ProjectState): void {
|
public changeState(newState: ProjectState): void {
|
||||||
@@ -161,4 +181,38 @@ export class OwnedProjectDetailComponent implements OnInit, OnDestroy {
|
|||||||
this.saveProject();
|
this.saveProject();
|
||||||
this.editstate = false;
|
this.editstate = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public openAddMember(): void {
|
||||||
|
const dialogRef = this.dialog.open(MemberCreateDialogComponent, {
|
||||||
|
data: {
|
||||||
|
creationType: CreationType.PROJECT_OWNED,
|
||||||
|
projectId: this.project.projectId,
|
||||||
|
},
|
||||||
|
width: '400px',
|
||||||
|
});
|
||||||
|
|
||||||
|
dialogRef.afterClosed().subscribe(resp => {
|
||||||
|
if (resp) {
|
||||||
|
const users: User.AsObject[] = resp.users;
|
||||||
|
const roles: string[] = resp.roles;
|
||||||
|
|
||||||
|
if (users && users.length && roles && roles.length) {
|
||||||
|
users.forEach(user => {
|
||||||
|
return this.projectService.AddProjectMember(this.projectId, user.id, roles)
|
||||||
|
.then(() => {
|
||||||
|
this.toast.showInfo('PROJECT.TOAST.MEMBERADDED', true);
|
||||||
|
}).catch(error => {
|
||||||
|
this.toast.showError(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public showDetail(): void {
|
||||||
|
if (this.project?.state === ProjectState.PROJECTSTATE_ACTIVE) {
|
||||||
|
this.router.navigate(['projects', this.project.projectId, 'members']);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -12,10 +12,11 @@ import { MatTableModule } from '@angular/material/table';
|
|||||||
import { MatTabsModule } from '@angular/material/tabs';
|
import { MatTabsModule } from '@angular/material/tabs';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
|
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
|
||||||
|
import { MemberCreateDialogModule } from 'src/app/modules/add-member-dialog/member-create-dialog.module';
|
||||||
import { CardModule } from 'src/app/modules/card/card.module';
|
import { CardModule } from 'src/app/modules/card/card.module';
|
||||||
import { ChangesModule } from 'src/app/modules/changes/changes.module';
|
import { ChangesModule } from 'src/app/modules/changes/changes.module';
|
||||||
|
import { ContributorsModule } from 'src/app/modules/contributors/contributors.module';
|
||||||
import { MetaLayoutModule } from 'src/app/modules/meta-layout/meta-layout.module';
|
import { MetaLayoutModule } from 'src/app/modules/meta-layout/meta-layout.module';
|
||||||
import { ProjectContributorsModule } from 'src/app/modules/project-contributors/project-contributors.module';
|
|
||||||
import { ProjectRolesModule } from 'src/app/modules/project-roles/project-roles.module';
|
import { ProjectRolesModule } from 'src/app/modules/project-roles/project-roles.module';
|
||||||
import { RefreshTableModule } from 'src/app/modules/refresh-table/refresh-table.module';
|
import { RefreshTableModule } from 'src/app/modules/refresh-table/refresh-table.module';
|
||||||
import { UserGrantsModule } from 'src/app/modules/user-grants/user-grants.module';
|
import { UserGrantsModule } from 'src/app/modules/user-grants/user-grants.module';
|
||||||
@@ -45,7 +46,7 @@ import { ProjectGrantsComponent } from './project-grants/project-grants.componen
|
|||||||
MatTabsModule,
|
MatTabsModule,
|
||||||
MatButtonModule,
|
MatButtonModule,
|
||||||
MatIconModule,
|
MatIconModule,
|
||||||
ProjectContributorsModule,
|
ContributorsModule,
|
||||||
WarnDialogModule,
|
WarnDialogModule,
|
||||||
ProjectRolesModule,
|
ProjectRolesModule,
|
||||||
HasRolePipeModule,
|
HasRolePipeModule,
|
||||||
@@ -61,6 +62,7 @@ import { ProjectGrantsComponent } from './project-grants/project-grants.componen
|
|||||||
ChangesModule,
|
ChangesModule,
|
||||||
MetaLayoutModule,
|
MetaLayoutModule,
|
||||||
RefreshTableModule,
|
RefreshTableModule,
|
||||||
|
MemberCreateDialogModule,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class OwnedProjectDetailModule { }
|
export class OwnedProjectDetailModule { }
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
<app-refresh-table *ngIf="projectId" (refreshed)="loadGrantsPage()" [dataSize]="dataSource.totalResult"
|
<app-refresh-table [loading]="dataSource?.loading$ | async" *ngIf="projectId" (refreshed)="loadGrantsPage()"
|
||||||
[selection]="selection">
|
[dataSize]="dataSource.totalResult" [selection]="selection">
|
||||||
<ng-template appHasRole [appHasRole]="['project.grant.member.delete:'+projectId, 'project.grant.member.delete']"
|
<ng-template appHasRole [appHasRole]="['project.grant.member.delete:'+projectId, 'project.grant.member.delete']"
|
||||||
actions>
|
actions>
|
||||||
<button (click)="deleteSelectedGrants()" [disabled]="disabled" mat-icon-button *ngIf="selection.hasValue()"
|
<button (click)="deleteSelectedGrants()" [disabled]="disabled" mat-icon-button *ngIf="selection.hasValue()"
|
||||||
@@ -16,9 +16,6 @@
|
|||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
<div class="table-wrapper">
|
<div class="table-wrapper">
|
||||||
<div class="spinner-container" *ngIf="dataSource.loading$ | async">
|
|
||||||
<mat-spinner diameter="50"></mat-spinner>
|
|
||||||
</div>
|
|
||||||
<table mat-table multiTemplateDataRows class="full-width-table" aria-label="Elements" [dataSource]="dataSource">
|
<table mat-table multiTemplateDataRows class="full-width-table" aria-label="Elements" [dataSource]="dataSource">
|
||||||
|
|
||||||
<ng-container matColumnDef="select">
|
<ng-container matColumnDef="select">
|
||||||
|
@@ -5,12 +5,6 @@
|
|||||||
.table-wrapper {
|
.table-wrapper {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
|
||||||
.spinner-container {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
table, mat-paginator {
|
table, mat-paginator {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background-color: #2d2e30;
|
background-color: #2d2e30;
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
<app-card title="{{'USER.MFA.TITLE' | translate}}" description="{{'USER.MFA.DESCRIPTION' | translate}}">
|
<app-card title="{{'USER.MFA.TITLE' | translate}}" description="{{'USER.MFA.DESCRIPTION' | translate}}">
|
||||||
<app-refresh-table (refreshed)="getOTP()" [dataSize]="dataSource?.data?.length">
|
<app-refresh-table [loading]="loading$ | async" (refreshed)="getOTP()" [dataSize]="dataSource?.data?.length">
|
||||||
<table class="background-style" mat-table [dataSource]="dataSource">
|
<table class="background-style" mat-table [dataSource]="dataSource">
|
||||||
<ng-container matColumnDef="type">
|
<ng-container matColumnDef="type">
|
||||||
<th mat-header-cell *matHeaderCellDef> {{ 'USER.MFA.TABLETYPE' | translate }} </th>
|
<th mat-header-cell *matHeaderCellDef> {{ 'USER.MFA.TABLETYPE' | translate }} </th>
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
<app-card title="{{'USER.MFA.TITLE' | translate}}" description="{{'USER.MFA.DESCRIPTION' | translate}}">
|
<app-card title="{{'USER.MFA.TITLE' | translate}}" description="{{'USER.MFA.DESCRIPTION' | translate}}">
|
||||||
<app-refresh-table (refreshed)="getOTP()" [dataSize]="dataSource?.data?.length">
|
<app-refresh-table [loading]="loading$ | async" (refreshed)="getOTP()" [dataSize]="dataSource?.data?.length">
|
||||||
<table class="background-style" mat-table [dataSource]="dataSource">
|
<table class="background-style" mat-table [dataSource]="dataSource">
|
||||||
<ng-container matColumnDef="type">
|
<ng-container matColumnDef="type">
|
||||||
<th mat-header-cell *matHeaderCellDef> {{ 'USER.MFA.TABLETYPE' | translate }} </th>
|
<th mat-header-cell *matHeaderCellDef> {{ 'USER.MFA.TABLETYPE' | translate }} </th>
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
<h1>{{ 'USER.PAGES.LIST' | translate }}</h1>
|
<h1>{{ 'USER.PAGES.LIST' | translate }}</h1>
|
||||||
<p class="sub">{{ 'USER.PAGES.DESCRIPTION' | translate }}</p>
|
<p class="sub">{{ 'USER.PAGES.DESCRIPTION' | translate }}</p>
|
||||||
|
|
||||||
<app-refresh-table (refreshed)="refreshPage()" [dataSize]="dataSource.data.length">
|
<app-refresh-table [loading]="loading$ | async" (refreshed)="refreshPage()" [dataSize]="dataSource.data.length">
|
||||||
<ng-template appHasRole [appHasRole]="['user.write']" actions>
|
<ng-template appHasRole [appHasRole]="['user.write']" actions>
|
||||||
<button (click)="deactivateSelectedUsers()" matTooltip="{{'ORG_DETAIL.TABLE.DEACTIVATE' | translate}}"
|
<button (click)="deactivateSelectedUsers()" matTooltip="{{'ORG_DETAIL.TABLE.DEACTIVATE' | translate}}"
|
||||||
class="icon-button" mat-icon-button *ngIf="selection.hasValue()">
|
class="icon-button" mat-icon-button *ngIf="selection.hasValue()">
|
||||||
@@ -18,9 +18,6 @@
|
|||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
<div class="table-wrapper">
|
<div class="table-wrapper">
|
||||||
<div class="spinner-container" *ngIf="loading$ | async">
|
|
||||||
<mat-spinner diameter="50"></mat-spinner>
|
|
||||||
</div>
|
|
||||||
<table class="background-style" mat-table [dataSource]="dataSource">
|
<table class="background-style" mat-table [dataSource]="dataSource">
|
||||||
<ng-container matColumnDef="select">
|
<ng-container matColumnDef="select">
|
||||||
<th mat-header-cell *matHeaderCellDef>
|
<th mat-header-cell *matHeaderCellDef>
|
||||||
|
@@ -14,12 +14,6 @@ h1 {
|
|||||||
.table-wrapper {
|
.table-wrapper {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
|
||||||
.spinner-container {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
table, mat-paginator {
|
table, mat-paginator {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
|
@@ -479,7 +479,8 @@
|
|||||||
},
|
},
|
||||||
"APP": {
|
"APP": {
|
||||||
"TITLE": "Applikationen",
|
"TITLE": "Applikationen",
|
||||||
"NAME": "Name"
|
"NAME": "Name",
|
||||||
|
"NAMEREQUIRED":"Der Name ist erforderlich."
|
||||||
},
|
},
|
||||||
"ROLE": {
|
"ROLE": {
|
||||||
"ADDNEWLINE":"Zusätzliche Rolle hinzufügen",
|
"ADDNEWLINE":"Zusätzliche Rolle hinzufügen",
|
||||||
@@ -531,7 +532,8 @@
|
|||||||
"TITLE": "Applikation",
|
"TITLE": "Applikation",
|
||||||
"DESCRIPTION": "Hier kannst du deine Applikation bearbeiten und deren Konfiguration anpassen.",
|
"DESCRIPTION": "Hier kannst du deine Applikation bearbeiten und deren Konfiguration anpassen.",
|
||||||
"CREATE_OIDC": "OIDC Applikation",
|
"CREATE_OIDC": "OIDC Applikation",
|
||||||
"CREATE_OIDC_DESc": "Gib deine Applikationsdaten ein!",
|
"CREATE_OIDC_DESC_TITLE": "Gib deine Applikationsdaten Schritt für Schritt ein",
|
||||||
|
"CREATE_OIDC_DESC_SUB": "Es wird automatisch eine empfohlene Konfiguration generiert",
|
||||||
"DETAIL": {
|
"DETAIL": {
|
||||||
"TITLE": "Detail",
|
"TITLE": "Detail",
|
||||||
"STATE": {
|
"STATE": {
|
||||||
@@ -542,16 +544,29 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"NAME": "Name",
|
"NAME": "Name",
|
||||||
|
"TYPE":"Applikationstyp",
|
||||||
|
"GRANT":"Grant Types",
|
||||||
"OIDC": {
|
"OIDC": {
|
||||||
|
"PROSWITCH":"Konfigurator überspringen",
|
||||||
|
"NAMEANDTYPESECTION":"Name und Typ",
|
||||||
|
"TITLEFIRST":"Geben Sie zuerst einen Namen ein!",
|
||||||
|
"TYPETITLE":"Welche Art von Applikation möchtest du erstellen?",
|
||||||
|
"REDIRECTTITLE":"Wohin soll nach dem Login weitergeleitet werden?",
|
||||||
|
"POSTREDIRECTTITLE":"Das ist die Weiterleitung nach einem logout.",
|
||||||
|
"TYPEREQUIRED":"Der Typ ist notwendig!",
|
||||||
"TITLE": "OIDC Konfiguration",
|
"TITLE": "OIDC Konfiguration",
|
||||||
"CLIENTID": "Client ID",
|
"CLIENTID": "Client ID",
|
||||||
"CLIENTSECRET": "Client Secret",
|
"CLIENTSECRET": "Client Secret",
|
||||||
"CLIENTSECRET_DESCRIPTION": "Speichere das Client Secret, da es verschwindet, sobald der Dialog geschlossen wird!",
|
"CLIENTSECRET_DESCRIPTION": "Speichere das Client Secret, da es verschwindet, sobald der Dialog geschlossen wird!",
|
||||||
"REGENERATESECRET": "Client Secret neu generieren",
|
"REGENERATESECRET": "Client Secret neu generieren",
|
||||||
"REDIRECT": "Redirect URIs",
|
"REDIRECT": "Redirect URIs",
|
||||||
|
"REDIRECTSECTION": "Redirect URIs",
|
||||||
"POSTLOGOUTREDIRECT":"Post logout URIs",
|
"POSTLOGOUTREDIRECT":"Post logout URIs",
|
||||||
"RESPONSE": "Response Types",
|
"RESPONSE": "Response Types",
|
||||||
|
"RESPONSESECTION": "Antwort Typen",
|
||||||
"GRANT": "Grant Types",
|
"GRANT": "Grant Types",
|
||||||
|
"GRANTSECTION":"Grant Typen",
|
||||||
|
"GRANTTITLE":"Wählen Sie Ihre Grant Typen aus. Hinweis: Implizit ist nur für Browserbasierte Anwendungen verfügbar.",
|
||||||
"APPTYPE": "App Types",
|
"APPTYPE": "App Types",
|
||||||
"RESPONSE0": "Code",
|
"RESPONSE0": "Code",
|
||||||
"RESPONSE1": "Id Token",
|
"RESPONSE1": "Id Token",
|
||||||
@@ -563,9 +578,13 @@
|
|||||||
"APPTYPE1": "User Agent",
|
"APPTYPE1": "User Agent",
|
||||||
"APPTYPE2": "Native",
|
"APPTYPE2": "Native",
|
||||||
"AUTHMETHOD":"Auth Method",
|
"AUTHMETHOD":"Auth Method",
|
||||||
|
"AUTHMETHODSECTION":"Authentifizierungsmethode",
|
||||||
"AUTHMETHOD0":"Basic",
|
"AUTHMETHOD0":"Basic",
|
||||||
"AUTHMETHOD1":"Post",
|
"AUTHMETHOD1":"Post",
|
||||||
"AUTHMETHOD2":"None"
|
"AUTHMETHOD2":"None",
|
||||||
|
"UNSECUREREDIRECT":"Ich hoffe du weisst was du tust",
|
||||||
|
"OVERVIEWSECTION":"Übersicht",
|
||||||
|
"OVERVIEWTITLE":"Deine Konfiguration ist bereit! Du kannst sie hier nochmals prüfen."
|
||||||
},
|
},
|
||||||
"TOAST": {
|
"TOAST": {
|
||||||
"REACTIVATED":"App reaktiviert!",
|
"REACTIVATED":"App reaktiviert!",
|
||||||
|
@@ -477,7 +477,8 @@
|
|||||||
},
|
},
|
||||||
"APP": {
|
"APP": {
|
||||||
"TITLE": "Applications",
|
"TITLE": "Applications",
|
||||||
"NAME": "Name"
|
"NAME": "Name",
|
||||||
|
"NAMEREQUIRED":"A name ist required."
|
||||||
},
|
},
|
||||||
"ROLE": {
|
"ROLE": {
|
||||||
"ADDNEWLINE":"Add additional role",
|
"ADDNEWLINE":"Add additional role",
|
||||||
@@ -529,7 +530,8 @@
|
|||||||
"TITLE": "Application",
|
"TITLE": "Application",
|
||||||
"DESCRIPTION": "Here you can edit your application data and its configuration",
|
"DESCRIPTION": "Here you can edit your application data and its configuration",
|
||||||
"CREATE_OIDC": "OIDC Application",
|
"CREATE_OIDC": "OIDC Application",
|
||||||
"CREATE_OIDC_DESc": "Enter your application details",
|
"CREATE_OIDC_DESC_TITLE": "Enter your application details step by step",
|
||||||
|
"CREATE_OIDC_DESC_SUB": "A recommended configuration will be automatically generated.",
|
||||||
"DETAIL": {
|
"DETAIL": {
|
||||||
"TITLE": "Detail",
|
"TITLE": "Detail",
|
||||||
"STATE": {
|
"STATE": {
|
||||||
@@ -540,16 +542,29 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"NAME": "Name",
|
"NAME": "Name",
|
||||||
|
"TYPE":"Application type",
|
||||||
|
"GRANT":"Grant Types",
|
||||||
"OIDC": {
|
"OIDC": {
|
||||||
|
"PROSWITCH":"I'm a pro. Skip this wizard.",
|
||||||
|
"NAMEANDTYPESECTION":"Name and Type",
|
||||||
|
"TITLEFIRST":"Insert a name first!",
|
||||||
|
"TYPETITLE":"What type of application do you want to create?",
|
||||||
|
"REDIRECTTITLE":"Specify the urls where the login will redirect to.",
|
||||||
|
"POSTREDIRECTTITLE":"This is the redirect after logout.",
|
||||||
|
"TYPEREQUIRED":"The type is required!",
|
||||||
"TITLE": "OIDC Configuration",
|
"TITLE": "OIDC Configuration",
|
||||||
"CLIENTID": "Client ID",
|
"CLIENTID": "Client ID",
|
||||||
"CLIENTSECRET": "Client Secret",
|
"CLIENTSECRET": "Client Secret",
|
||||||
"CLIENTSECRET_DESCRIPTION": "Save your Client Secret as it will disappear once the dialog is closed!",
|
"CLIENTSECRET_DESCRIPTION": "Save your Client Secret as it will disappear once the dialog is closed!",
|
||||||
"REGENERATESECRET": "Regenerate Client Secret",
|
"REGENERATESECRET": "Regenerate Client Secret",
|
||||||
"REDIRECT": "Redirect URIs",
|
"REDIRECT": "Redirect URIs",
|
||||||
|
"REDIRECTSECTION": "Redirect URIs",
|
||||||
"POSTLOGOUTREDIRECT":"Post logout URIs",
|
"POSTLOGOUTREDIRECT":"Post logout URIs",
|
||||||
"RESPONSE": "Response Types",
|
"RESPONSE": "Response Types",
|
||||||
|
"RESPONSESECTION": "Response Types",
|
||||||
"GRANT": "Grant Types",
|
"GRANT": "Grant Types",
|
||||||
|
"GRANTSECTION":"Grant Types",
|
||||||
|
"GRANTTITLE":"Select your grant types. Note: Implicit is only available for browser based applications.",
|
||||||
"APPTYPE": "App Types",
|
"APPTYPE": "App Types",
|
||||||
"RESPONSE0": "Code",
|
"RESPONSE0": "Code",
|
||||||
"RESPONSE1": "Id Token",
|
"RESPONSE1": "Id Token",
|
||||||
@@ -561,9 +576,13 @@
|
|||||||
"APPTYPE1": "User Agent",
|
"APPTYPE1": "User Agent",
|
||||||
"APPTYPE2": "Native",
|
"APPTYPE2": "Native",
|
||||||
"AUTHMETHOD":"Auth Method",
|
"AUTHMETHOD":"Auth Method",
|
||||||
|
"AUTHMETHODSECTION":"Auth Method",
|
||||||
"AUTHMETHOD0":"Basic",
|
"AUTHMETHOD0":"Basic",
|
||||||
"AUTHMETHOD1":"Post",
|
"AUTHMETHOD1":"Post",
|
||||||
"AUTHMETHOD2":"None"
|
"AUTHMETHOD2":"None",
|
||||||
|
"UNSECUREREDIRECT":"I sure hope you know what you are doing.",
|
||||||
|
"OVERVIEWSECTION":"Overview",
|
||||||
|
"OVERVIEWTITLE":"You are now done. Review your configuration"
|
||||||
},
|
},
|
||||||
"TOAST": {
|
"TOAST": {
|
||||||
"REACTIVATED":"App reactivated!",
|
"REACTIVATED":"App reactivated!",
|
||||||
|
@@ -72,7 +72,7 @@ $caos-light-brand: (
|
|||||||
A500:#333333,
|
A500:#333333,
|
||||||
A600: #000000,
|
A600: #000000,
|
||||||
A700: #8795a1,
|
A700: #8795a1,
|
||||||
A800: #fafafa,
|
A800: white,
|
||||||
A900: #fafafa,
|
A900: #fafafa,
|
||||||
contrast: (
|
contrast: (
|
||||||
50: #3d4852,
|
50: #3d4852,
|
||||||
@@ -155,8 +155,8 @@ $custom-typography: mat-typography-config(
|
|||||||
color: #202124;
|
color: #202124;
|
||||||
|
|
||||||
.sidenav, .main-container {
|
.sidenav, .main-container {
|
||||||
background-color: white;
|
background-color: #fafafa;
|
||||||
transition: background-color .5s ease-in-out;
|
transition: background-color .4s ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar-track {
|
::-webkit-scrollbar-track {
|
||||||
@@ -185,11 +185,7 @@ $custom-typography: mat-typography-config(
|
|||||||
|
|
||||||
.sidenav, .main-container {
|
.sidenav, .main-container {
|
||||||
background-color: #212224;
|
background-color: #212224;
|
||||||
transition: background-color .5s ease-in-out;
|
transition: background-color .4s ease-in-out;
|
||||||
}
|
|
||||||
|
|
||||||
.mat-dialog-container {
|
|
||||||
background-color: #2d2e30;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar-track {
|
::-webkit-scrollbar-track {
|
||||||
|
@@ -12,11 +12,12 @@
|
|||||||
|
|
||||||
.card {
|
.card {
|
||||||
background-color: $primary-dark;
|
background-color: $primary-dark;
|
||||||
transition: background-color .5s ease-in-out;
|
transition: background-color .4s ease-in-out;
|
||||||
border: 1px solid rgba($border-color, .2);
|
border: 1px solid rgba($border-color, .2);
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
border-radius: .5rem;
|
border-radius: .5rem;
|
||||||
outline: none;
|
outline: none;
|
||||||
|
// box-shadow: 0px 2px 1px -1px rgba(0, 0, 0, 0.1), 0px 1px 1px 0px rgba(0, 0, 0, 0.1), 0px 1px 3px 0px rgba(0, 0, 0, 0.1);
|
||||||
|
|
||||||
.selection-icon {
|
.selection-icon {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
|
@@ -2,9 +2,10 @@
|
|||||||
|
|
||||||
@mixin changes-theme($theme) {
|
@mixin changes-theme($theme) {
|
||||||
$primary: map-get($theme, primary);
|
$primary: map-get($theme, primary);
|
||||||
$primary-dark: mat-color($primary, A600);
|
$primary-dark: mat-color($primary, A800);
|
||||||
|
|
||||||
.change-item-back {
|
.change-item-back {
|
||||||
background-color: rgba($primary-dark, 0.03);
|
background-color: rgba($primary-dark, 0.93);
|
||||||
|
transition: background-color .4s ease-in-out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -5,7 +5,7 @@
|
|||||||
$primary-color: mat-color($primary, 500);
|
$primary-color: mat-color($primary, 500);
|
||||||
$primary-dark: mat-color($primary, A800);
|
$primary-dark: mat-color($primary, A800);
|
||||||
|
|
||||||
$lighter-color: mat-color($primary, 300);
|
$lighter-color: rgba(mat-color($primary, 300),.5);
|
||||||
|
|
||||||
.meta-wrapper {
|
.meta-wrapper {
|
||||||
.meta {
|
.meta {
|
||||||
|
@@ -19,7 +19,7 @@
|
|||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
border: 1px solid $accent-color;
|
border: 1px solid $accent-color;
|
||||||
background-color: $primary-dark;
|
background-color: $primary-dark;
|
||||||
transition: background-color .5s ease-in-out;
|
transition: background-color .4s ease-in-out;
|
||||||
|
|
||||||
|
|
||||||
&.add {
|
&.add {
|
||||||
|
@@ -50,7 +50,7 @@
|
|||||||
.root-header {
|
.root-header {
|
||||||
box-shadow: 0px 2px 1px -1px rgba(0, 0, 0, 0.1), 0px 1px 1px 0px rgba(0, 0, 0, 0.1), 0px 1px 3px 0px rgba(0, 0, 0, 0.1);
|
box-shadow: 0px 2px 1px -1px rgba(0, 0, 0, 0.1), 0px 1px 1px 0px rgba(0, 0, 0, 0.1), 0px 1px 3px 0px rgba(0, 0, 0, 0.1);
|
||||||
background-color: $primary-dark !important;
|
background-color: $primary-dark !important;
|
||||||
transition: background-color .5s ease-in-out;
|
transition: background-color .4s ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
.admin-line {
|
.admin-line {
|
||||||
@@ -75,6 +75,7 @@
|
|||||||
left: 0;
|
left: 0;
|
||||||
border-bottom: 20px solid $primary-dark;
|
border-bottom: 20px solid $primary-dark;
|
||||||
border-right: 20px solid transparent;
|
border-right: 20px solid transparent;
|
||||||
|
transition: border-color .4s ease-in-out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -9,7 +9,7 @@
|
|||||||
.mat-table,
|
.mat-table,
|
||||||
mat-paginator {
|
mat-paginator {
|
||||||
background-color: inherit !important;
|
background-color: inherit !important;
|
||||||
transition: background-color .5s ease-in-out;
|
transition: background-color .4s ease-in-out;
|
||||||
|
|
||||||
&.background-style {
|
&.background-style {
|
||||||
background-color: inherit !important;
|
background-color: inherit !important;
|
||||||
|
@@ -1,27 +1,21 @@
|
|||||||
@import '~@angular/material/theming';
|
@import '~@angular/material/theming';
|
||||||
|
|
||||||
@mixin theme-card($theme) {
|
@mixin theme-card($theme) {
|
||||||
$accent: map-get($theme, accent);
|
|
||||||
$background: map-get($theme, background);
|
|
||||||
$background-color: mat-color($background, card);
|
|
||||||
$primary: map-get($theme, primary);
|
$primary: map-get($theme, primary);
|
||||||
$primary-color: mat-color($primary, 500);
|
|
||||||
$primary-dark: mat-color($primary, A800);
|
$primary-dark: mat-color($primary, A800);
|
||||||
$border-color: mat-color($primary, A700);
|
|
||||||
$border-selected-color: mat-color($primary, A600);
|
|
||||||
|
|
||||||
.theme-conent {
|
.theme-conent {
|
||||||
background-color: $primary-dark;
|
background-color: $primary-dark;
|
||||||
transition: background-color .5s ease-in-out;
|
transition: background-color .4s ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
.theme-app {
|
.theme-app {
|
||||||
background-color: $primary-dark;
|
background-color: $primary-dark;
|
||||||
transition: background-color .5s ease-in-out;
|
transition: background-color .4s ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
.crescent {
|
.crescent {
|
||||||
background-color: $primary-dark;
|
background-color: $primary-dark;
|
||||||
transition: background background-color .5s ease-in-out;
|
transition: background-color .4s ease-in-out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user