mirror of
https://github.com/andwati/zitadel.git
synced 2026-11-06 22:32:16 +00:00
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:
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user