mirror of
https://github.com/zitadel/zitadel.git
synced 2025-12-27 06:06:29 +00:00
feat(console): integrate frontend (#95)
* feat: console frontend * chore(dependabot): cycle and npm * chore: rename citadel to zitadel, remove generated files * chore: delete go files * chore(frontend): ci steps * chore: remove docker and envoy files * chore: remove docker file * chore: working dir * chore: run proto build * add console start * chore: restructure folders * chore: remove gui build * statikFs * generate proto for console * add statik import * import * chore: try statik * chore: path * chore: path * chore: script in root * chore: order build steps * chore: go get * chore: folder traversal * chore: non empty test file * chore: gitignore * chore: gitignore * chore: statik path * chore: switch to failing FE build * fix: build * fix: project-grant-test * fix: rm test * add statik.go * go mod tidy * chore: place test, seperate test from build * chore: lint all the world * chore: ci the world instead * chore: tune docker * chore: undo container test * chore: fix run * chore: docker build * chore: test docker build * chore: go build flags * finaly * fix caos_local * go mod Co-authored-by: Livio Amstutz <livio.a@gmail.com> Co-authored-by: Max Peintner <max@caos.ch>
This commit is contained in:
13
console/src/app/modules/changes/changes.component.html
Normal file
13
console/src/app/modules/changes/changes.component.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<span class="header">{{ 'CHANGES.LISTTITLE' | translate }}</span>
|
||||
|
||||
<div class="scroll-container" appScrollable (scrollPosition)="scrollHandler($event)">
|
||||
<li class="item change-item-back" *ngFor="let event of data | async">
|
||||
<span class="seq">
|
||||
{{dateFromTimestamp(event.changeDate) | localizedDate: 'EEE dd. MMM, HH:mm'}}
|
||||
</span>
|
||||
<span class="desc">{{'CHANGES.EVENTS.'+event.eventType | translate}}</span>
|
||||
</li>
|
||||
<div class="sp-wrapper">
|
||||
<mat-spinner *ngIf="loading | async" diameter="25"></mat-spinner>
|
||||
</div>
|
||||
</div>
|
||||
40
console/src/app/modules/changes/changes.component.scss
Normal file
40
console/src/app/modules/changes/changes.component.scss
Normal file
@@ -0,0 +1,40 @@
|
||||
.header {
|
||||
display: block;
|
||||
margin-bottom: 1rem;
|
||||
font-weight: 400;
|
||||
color: #81868a;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.scroll-container {
|
||||
max-height: 540px;
|
||||
overflow-y: scroll;
|
||||
|
||||
.item {
|
||||
box-sizing: border-box;
|
||||
height: 60px;
|
||||
padding: .5rem;
|
||||
margin: .25rem 0;
|
||||
border-radius: .5rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.seq {
|
||||
color: #81868a;
|
||||
font-size: 12px;
|
||||
align-self: flex-end;
|
||||
}
|
||||
|
||||
.desc {
|
||||
overflow-x: auto;
|
||||
font-size: .9rem;
|
||||
margin: 4px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.sp-wrapper {
|
||||
padding: .5rem;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
25
console/src/app/modules/changes/changes.component.spec.ts
Normal file
25
console/src/app/modules/changes/changes.component.spec.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ChangesComponent } from './changes.component';
|
||||
|
||||
describe('ChangesComponent', () => {
|
||||
let component: ChangesComponent;
|
||||
let fixture: ComponentFixture<ChangesComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ChangesComponent],
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ChangesComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
124
console/src/app/modules/changes/changes.component.ts
Normal file
124
console/src/app/modules/changes/changes.component.ts
Normal file
@@ -0,0 +1,124 @@
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb';
|
||||
import { BehaviorSubject, from, Observable } from 'rxjs';
|
||||
import { scan, take, tap } from 'rxjs/operators';
|
||||
import { Change, Changes } from 'src/app/proto/generated/management_pb';
|
||||
import { MgmtUserService } from 'src/app/services/mgmt-user.service';
|
||||
|
||||
export enum ChangeType {
|
||||
USER = 'user',
|
||||
ORG = 'org',
|
||||
PROJECT = 'project',
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-changes',
|
||||
templateUrl: './changes.component.html',
|
||||
styleUrls: ['./changes.component.scss'],
|
||||
})
|
||||
export class ChangesComponent implements OnInit {
|
||||
@Input() public changeType: ChangeType = ChangeType.USER;
|
||||
@Input() public id: string = '';
|
||||
|
||||
// Source data
|
||||
private _done: BehaviorSubject<any> = new BehaviorSubject(false);
|
||||
private _loading: BehaviorSubject<any> = new BehaviorSubject(false);
|
||||
private _data: BehaviorSubject<any> = new BehaviorSubject([]);
|
||||
|
||||
// Observable data
|
||||
loading: Observable<boolean> = this._loading.asObservable();
|
||||
public data!: Observable<Change.AsObject[]>;
|
||||
public changes!: Changes.AsObject;
|
||||
constructor(private mgmtUserService: MgmtUserService) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.init();
|
||||
}
|
||||
|
||||
public scrollHandler(e: any): void {
|
||||
if (e === 'bottom') {
|
||||
this.more();
|
||||
}
|
||||
}
|
||||
|
||||
private init(): void {
|
||||
let first: Promise<Changes>;
|
||||
switch (this.changeType) {
|
||||
case ChangeType.USER: first = this.mgmtUserService.UserChanges(this.id, 10, 0);
|
||||
break;
|
||||
case ChangeType.PROJECT: first = this.mgmtUserService.ProjectChanges(this.id, 20, 0);
|
||||
break;
|
||||
case ChangeType.ORG: first = this.mgmtUserService.OrgChanges(this.id, 10, 0);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
this.mapAndUpdate(first);
|
||||
|
||||
// Create the observable array for consumption in components
|
||||
this.data = this._data.asObservable().pipe(
|
||||
scan((acc, val) => {
|
||||
return false ? val.concat(acc) : acc.concat(val);
|
||||
}));
|
||||
}
|
||||
|
||||
private more(): void {
|
||||
const cursor = this.getCursor();
|
||||
|
||||
let more: Promise<Changes>;
|
||||
|
||||
switch (this.changeType) {
|
||||
case ChangeType.USER: more = this.mgmtUserService.UserChanges(this.id, 10, cursor);
|
||||
break;
|
||||
case ChangeType.PROJECT: more = this.mgmtUserService.ProjectChanges(this.id, 10, cursor);
|
||||
break;
|
||||
case ChangeType.ORG: more = this.mgmtUserService.OrgChanges(this.id, 10, cursor);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
this.mapAndUpdate(more);
|
||||
}
|
||||
|
||||
// Determines the snapshot to paginate query
|
||||
private getCursor(): number {
|
||||
const current = this._data.value;
|
||||
if (current.length) {
|
||||
// return true ? current[0].sequence :
|
||||
return current[current.length - 1].sequence;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Maps the snapshot to usable format the updates source
|
||||
private mapAndUpdate(col: Promise<Changes>): any {
|
||||
if (this._done.value || this._loading.value) { return; }
|
||||
|
||||
// loading
|
||||
this._loading.next(true);
|
||||
|
||||
// Map snapshot with doc ref (needed for cursor)
|
||||
return from(col).pipe(
|
||||
tap((res: Changes) => {
|
||||
let values = res.toObject().changesList;
|
||||
// If prepending, reverse the batch order
|
||||
values = false ? values.reverse() : values;
|
||||
|
||||
// update source with new values, done loading
|
||||
this._data.next(values);
|
||||
// console.log(values);
|
||||
this._loading.next(false);
|
||||
|
||||
// no more values, mark done
|
||||
if (!values.length) {
|
||||
this._done.next(true);
|
||||
}
|
||||
}),
|
||||
take(1)).subscribe();
|
||||
}
|
||||
|
||||
public dateFromTimestamp(date: Timestamp.AsObject): any {
|
||||
const ts: Date = new Date(date.seconds * 1000 + date.nanos / 1000);
|
||||
return ts;
|
||||
}
|
||||
}
|
||||
30
console/src/app/modules/changes/changes.module.ts
Normal file
30
console/src/app/modules/changes/changes.module.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { ScrollingModule } from '@angular/cdk/scrolling';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { ScrollableModule } from 'src/app/directives/scrollable/scrollable.module';
|
||||
import { PipesModule } from 'src/app/pipes/pipes.module';
|
||||
|
||||
import { ChangesComponent } from './changes.component';
|
||||
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
ChangesComponent,
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
ScrollableModule,
|
||||
MatProgressSpinnerModule,
|
||||
TranslateModule,
|
||||
PipesModule,
|
||||
ScrollingModule,
|
||||
],
|
||||
exports: [
|
||||
ChangesComponent,
|
||||
ScrollableModule,
|
||||
],
|
||||
})
|
||||
export class ChangesModule { }
|
||||
Reference in New Issue
Block a user