feat(console): show and clear admin views, fix iam member (#334)

* gen auth, get my user changes

* iam view

* use table for admin views

* fix pagination for views

* fix themeing transition

* transition

* lint
This commit is contained in:
Max Peintner
2020-07-02 17:16:47 +02:00
committed by GitHub
parent 5b17b2641a
commit c8e2db3287
30 changed files with 1293 additions and 49 deletions
@@ -5,7 +5,7 @@
<p class="desc">{{ 'IAM.MEMBER.DESCRIPTION' | translate }}</p>
</div>
<div class="table-header-row" *ngIf="org">
<div class="table-header-row">
<div class="col">
<ng-container *ngIf="!selection.hasValue()">
<span class="desc">{{'ORG_DETAIL.TABLE.TOTAL' | translate}}</span>
@@ -17,13 +17,14 @@
</ng-container>
</div>
<span class="fill-space"></span>
<ng-template appHasRole [appHasRole]="['org.member.delete:'+org.id,'org.member.delete']">
<button (click)="removeProjectMemberSelection()" matTooltip="{{'ORG_DETAIL.TABLE.DELETE' | translate}}"
class="icon-button" mat-icon-button *ngIf="selection.hasValue()">
<ng-template appHasRole [appHasRole]="['iam.member.delete']">
<button color="warn" (click)="removeProjectMemberSelection()"
matTooltip="{{'ORG_DETAIL.TABLE.DELETE' | translate}}" class="icon-button" mat-icon-button
*ngIf="selection.hasValue()">
<i class="las la-trash"></i>
</button>
</ng-template>
<ng-template appHasRole [appHasRole]="['org.member.write:'+org.id,'org.member.write']">
<ng-template appHasRole [appHasRole]="['org.member.write']">
<a color="primary" [disabled]="disabled" class="add-button" (click)="openAddMember()" color="primary"
mat-raised-button>
<mat-icon class="icon">add</mat-icon>{{ 'ACTIONS.NEW' | translate }}
@@ -5,7 +5,7 @@ import { MatPaginator } from '@angular/material/paginator';
import { MatTable } from '@angular/material/table';
import { tap } from 'rxjs/operators';
import { CreationType, MemberCreateDialogComponent } from 'src/app/modules/add-member-dialog/member-create-dialog.component';
import { Org, ProjectMember, ProjectType, User } from 'src/app/proto/generated/management_pb';
import { ProjectMember, ProjectType, User } from 'src/app/proto/generated/management_pb';
import { AdminService } from 'src/app/services/admin.service';
import { ToastService } from 'src/app/services/toast.service';
@@ -17,7 +17,6 @@ import { IamMembersDataSource } from './iam-members-datasource';
styleUrls: ['./iam-members.component.scss'],
})
export class IamMembersComponent implements AfterViewInit {
public org!: Org.AsObject;
public projectType: ProjectType = ProjectType.PROJECTTYPE_OWNED;
public disabled: boolean = false;
@ViewChild(MatPaginator) public paginator!: MatPaginator;
@@ -12,6 +12,7 @@ import { MatSortModule } from '@angular/material/sort';
import { MatTableModule } from '@angular/material/table';
import { MatTooltipModule } from '@angular/material/tooltip';
import { TranslateModule } from '@ngx-translate/core';
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
import { IamMembersRoutingModule } from './iam-members-routing.module';
import { IamMembersComponent } from './iam-members.component';
@@ -24,6 +25,7 @@ import { IamMembersComponent } from './iam-members.component';
CommonModule,
MatAutocompleteModule,
MatChipsModule,
HasRoleModule,
MatButtonModule,
MatCheckboxModule,
MatIconModule,
@@ -0,0 +1,46 @@
<div class="table-header-row">
<div class="col">
<span class="desc">{{'ORG_DETAIL.TABLE.TOTAL' | translate}}</span>
<span class="count">{{dataSource?.data?.length}}</span>
</div>
<span class="fill-space"></span>
</div>
<div class="table-wrapper">
<div class="spinner-container" *ngIf="loading$ | async">
<mat-spinner diameter="50"></mat-spinner>
</div>
<table *ngIf="dataSource" [dataSource]="dataSource" mat-table class="full-width-table background-style" matSort
aria-label="Elements">
<ng-container matColumnDef="viewName">
<th mat-header-cell mat-sort-header *matHeaderCellDef> {{ 'IAM.VIEWS.VIEWNAME' | translate }} </th>
<td mat-cell *matCellDef="let role"> {{role.viewName}} </td>
</ng-container>
<ng-container matColumnDef="database">
<th mat-header-cell mat-sort-header *matHeaderCellDef> {{ 'IAM.VIEWS.DATABASE' | translate }} </th>
<td mat-cell *matCellDef="let role"> {{role.database}} </td>
</ng-container>
<ng-container matColumnDef="sequence">
<th mat-header-cell mat-sort-header *matHeaderCellDef> {{ 'IAM.VIEWS.SEQUENCE' | translate }} </th>
<td mat-cell *matCellDef="let role">
<span class="role app-label">{{role.sequence}}</span>
</td>
</ng-container>
<ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef> {{ 'IAM.VIEWS.ACTIONS' | translate }} </th>
<td mat-cell *matCellDef="let role">
<button mat-icon-button matTooltip="{{'IAM.VIEWS.CLEAR' | translate}}"
(click)="cancelView(role.viewName, role.database)"><i class="las la-redo-alt"></i></button>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
<mat-paginator class="background-style" #paginator [pageSize]="10" [pageSizeOptions]="[10, 20, 100, 250]">
</mat-paginator>
</div>
@@ -0,0 +1,60 @@
.table-header-row {
display: flex;
align-items: center;
.col {
display: flex;
flex-direction: column;
.desc {
font-size: .8rem;
color: #8795a1;
}
.count {
font-size: 2rem;
}
}
.fill-space {
flex: 1;
}
.icon-button {
margin-right: .5rem;
}
}
.table-wrapper {
overflow: auto;
.spinner-container {
display: flex;
align-items: center;
justify-content: center;
}
table, mat-paginator {
width: 100%;
td, th {
&:first-child {
padding-left: 0;
padding-right: 1rem;
}
&:last-child {
padding-right: 0;
}
}
.selection {
width: 50px;
max-width: 50px;
}
}
}
.pointer {
outline: none;
cursor: pointer;
}
@@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { IamViewsComponent } from './iam-views.component';
describe('IamViewsComponent', () => {
let component: IamViewsComponent;
let fixture: ComponentFixture<IamViewsComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [IamViewsComponent],
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(IamViewsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,48 @@
import { Component, ViewChild } from '@angular/core';
import { MatPaginator } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort';
import { MatTable, MatTableDataSource } from '@angular/material/table';
import { BehaviorSubject, from, Observable, of } from 'rxjs';
import { catchError, finalize, map } from 'rxjs/operators';
import { View } from 'src/app/proto/generated/admin_pb';
import { AdminService } from 'src/app/services/admin.service';
@Component({
selector: 'app-iam-views',
templateUrl: './iam-views.component.html',
styleUrls: ['./iam-views.component.scss'],
})
export class IamViewsComponent {
public views: View.AsObject[] = [];
@ViewChild(MatPaginator) public paginator!: MatPaginator;
@ViewChild(MatTable) public table!: MatTable<View.AsObject>;
@ViewChild(MatSort) public sort!: MatSort;
public dataSource!: MatTableDataSource<View.AsObject>;
public displayedColumns: string[] = ['viewName', 'database', 'sequence', 'actions'];
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
public loading$: Observable<boolean> = this.loadingSubject.asObservable();
constructor(private adminService: AdminService) {
this.loadViews();
}
public loadViews(): void {
this.loadingSubject.next(true);
from(this.adminService.GetViews()).pipe(
map(resp => {
return resp.toObject().viewsList;
}),
catchError(() => of([])),
finalize(() => this.loadingSubject.next(false)),
).subscribe(views => {
this.dataSource = new MatTableDataSource(views);
this.dataSource.paginator = this.paginator;
});
}
public cancelView(viewname: string, db: string): void {
this.adminService.ClearView(viewname, db);
}
}
+4 -4
View File
@@ -1,10 +1,10 @@
<app-meta-layout>
<div class="enlarged-container">
<h1>{{'IAM.DETAIL.TITLE' | translate}}</h1>
<p class="sub">{{'IAM.DETAIL.DESCRIPTION' | translate}}
</p>
<app-card title="{{ 'ORG.DOMAINS.TITLE' | translate }}"
description="{{ 'ORG.DOMAINS.DESCRIPTION' | translate }}">
<p class="sub">{{'IAM.DETAIL.DESCRIPTION' | translate}} </p>
<app-card title="{{ 'IAM.VIEWS.TITLE' | translate }}" description="{{ 'IAM.VIEWS.DESCRIPTION' | translate }}">
<app-iam-views></app-iam-views>
</app-card>
</div>
+2 -1
View File
@@ -22,11 +22,12 @@ import { MetaLayoutModule } from 'src/app/modules/meta-layout/meta-layout.module
import { IamContributorsModule } from './iam-contributors/iam-contributors.module';
import { IamRoutingModule } from './iam-routing.module';
import { IamComponent } from './iam.component';
import { IamViewsComponent } from './iam-views/iam-views.component';
@NgModule({
declarations: [IamComponent],
declarations: [IamComponent, IamViewsComponent],
imports: [
CommonModule,
IamRoutingModule,
@@ -230,6 +230,6 @@
</div>
<!-- <p class="side-section">{{ 'CHANGES.USER.TITLE' | translate }}</p> -->
<app-changes [changeType]="ChangeType.USER" [id]="user.id"></app-changes>
<app-changes [changeType]="ChangeType.MYUSER" [id]="user.id"></app-changes>
</metainfo>
</app-meta-layout>
@@ -1,6 +1,6 @@
<input type="checkbox" id="switch" (change)="change($event.target.checked)" [(checked)]="isDarkTheme">
<div class="app">
<div class="content">
<div class="theme-app">
<div class="theme-content">
<div class="circle">
<div class="crescent"></div>
</div>
@@ -1,14 +1,20 @@
$dark-background: #2d2e30;
.app {
position: relative;
// * {
// transition: background-color .5s ease-in-out;
// }
:root {
transition: none;
}
.content {
.theme-app {
position: relative;
}
.theme-content {
display: flex;
flex-direction: column;
text-align: center;
// width: 300px;
}
.circle {
position: relative;
@@ -24,7 +30,7 @@ $dark-background: #2d2e30;
right: 0;
width: 4rem;
height: 4rem;
background: white;
background: #fafafa;
transform: scale(0);
transform-origin: top right;
transition: transform .6s cubic-bezier(0.645, 0.045, 0.355, 1);
@@ -58,9 +64,8 @@ label {
}
.toggle {
position: absolute;
background-color: #fff;
background-color: #fafafa;
box-shadow: 0 2px 15px rgba(0,0,0,.15);
transition: transform .3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
.names {
font-size: 90%;
@@ -79,45 +84,33 @@ label {
display: none;
}
/* Toggle */
[type="checkbox"]:checked + .app .toggle{
[type="checkbox"]:checked + .theme-app .toggle{
transform: translateX(100%);
background-color: $dark-background;
}
[type="checkbox"]:checked + .app .dark{
[type="checkbox"]:checked + .theme-app .dark{
opacity: 1;
}
[type="checkbox"]:checked + .app .light{
[type="checkbox"]:checked + .theme-app .light{
opacity: .5;
}
/* App */
[type="checkbox"]:checked + .app{
[type="checkbox"]:checked + .theme-app{
background-color: $dark-background;
color: white;
color: #fafafa;
}
/* Circle */
[type="checkbox"]:checked + .app .crescent{
[type="checkbox"]:checked + .theme-app .crescent{
transform: scale(1);
background: $dark-background;
}
[type="checkbox"]:checked + .app .circle{
[type="checkbox"]:checked + .theme-app .circle{
background: linear-gradient(40deg, #8983F7, #A3DAFB 70%);
}
[type="checkbox"]:checked + .app .main-circle{
[type="checkbox"]:checked + .theme-app .main-circle{
background: linear-gradient(40deg, #8983F7, #A3DAFB 70%);
}
/* Fab */
[type="checkbox"]:checked + .app .fab{
[type="checkbox"]:checked + .theme-app .fab{
background-color: $dark-background;
}
[type="checkbox"]:checked + .app .arrow,
[type="checkbox"]:checked + .app .mark,
[type="checkbox"]:checked + .app .battery{
background-color: white;
}
[type="checkbox"]:checked + .app .network{
border-color: transparent transparent white transparent;
}
[type="checkbox"]:checked + .app .swipe{
background-color: $dark-background;
opacity: 1;
}