fix(console): refactoring (#197)

* return error from changes

* project member context, org-policies, state

* project type seperation

* chore(deps): bump grpc from 1.24.2 to 1.24.3 in /console (#183)

Bumps [grpc](https://github.com/grpc/grpc-node) from 1.24.2 to 1.24.3.
- [Release notes](https://github.com/grpc/grpc-node/releases)
- [Commits](https://github.com/grpc/grpc-node/compare/grpc@1.24.2...grpc@1.24.3)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): bump google-proto-files from 1.1.2 to 2.1.0 in /console (#176)

Bumps [google-proto-files](https://github.com/googleapis/nodejs-proto-files) from 1.1.2 to 2.1.0.
- [Release notes](https://github.com/googleapis/nodejs-proto-files/releases)
- [Changelog](https://github.com/googleapis/nodejs-proto-files/blob/master/CHANGELOG.md)
- [Commits](https://github.com/googleapis/nodejs-proto-files/compare/v1.1.2...v2.1.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps-dev): bump karma-coverage-istanbul-reporter in /console (#169)

Bumps [karma-coverage-istanbul-reporter](https://github.com/mattlewis92/karma-coverage-istanbul-reporter) from 3.0.2 to 3.0.3.
- [Release notes](https://github.com/mattlewis92/karma-coverage-istanbul-reporter/releases)
- [Changelog](https://github.com/mattlewis92/karma-coverage-istanbul-reporter/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mattlewis92/karma-coverage-istanbul-reporter/compare/v3.0.2...v3.0.3)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* update packages

* update deps

* lint

* replace assets

* add key, creationdate for roles

* project grant members

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This commit is contained in:
Max Peintner 2020-06-10 12:59:12 +02:00 committed by GitHub
parent e0fb19b4e9
commit 2d369fbcd3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
51 changed files with 1051 additions and 1376 deletions

1952
console/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -14,53 +14,53 @@
},
"private": true,
"dependencies": {
"@angular/animations": "~9.1.0",
"@angular/cdk": "~9.0.1",
"@angular/common": "~9.1.0",
"@angular/compiler": "~9.1.0",
"@angular/core": "~9.1.0",
"@angular/forms": "~9.1.0",
"@angular/material": "^9.0.1",
"@angular/platform-browser": "~9.1.0",
"@angular/platform-browser-dynamic": "~9.1.0",
"@angular/router": "~9.1.0",
"@angular/service-worker": "~9.1.0",
"@angular/animations": "~9.1.10",
"@angular/cdk": "~9.2.4",
"@angular/common": "~9.1.10",
"@angular/compiler": "~9.1.10",
"@angular/core": "~9.1.10",
"@angular/forms": "~9.1.10",
"@angular/material": "^9.2.4",
"@angular/platform-browser": "~9.1.10",
"@angular/platform-browser-dynamic": "~9.1.10",
"@angular/router": "~9.1.10",
"@angular/service-worker": "~9.1.10",
"@ngx-translate/core": "^12.1.2",
"@ngx-translate/http-loader": "^4.0.0",
"@types/google-protobuf": "^3.7.2",
"@types/uuid": "^8.0.0",
"angular-oauth2-oidc": "^8.0.4",
"angularx-qrcode": "^2.1.0",
"angular-oauth2-oidc": "^9.2.2",
"angularx-qrcode": "^2.3.4",
"cors": "^2.8.5",
"google-proto-files": "^1.1.1",
"google-protobuf": "^3.12.0",
"grpc": "^1.24.2",
"google-proto-files": "^2.1.0",
"google-protobuf": "^3.12.2",
"grpc": "^1.24.3",
"grpc-web": "^1.1.0",
"hammerjs": "^2.0.8",
"moment": "^2.24.0",
"moment": "^2.26.0",
"ngx-moment": "^3.5.0",
"prettier-stylelint": "^0.4.2",
"rxjs": "~6.5.5",
"ts-protoc-gen": "^0.12.0",
"tslib": "^1.13.0",
"uuid": "^7.0.1",
"tslib": "^2.0.0",
"uuid": "^8.1.0",
"zone.js": "~0.10.3"
},
"devDependencies": {
"@angular-devkit/build-angular": "~0.901.7",
"@angular/cli": "~9.1.0",
"@angular/compiler-cli": "~9.1.0",
"@angular/language-service": "~9.1.9",
"@angular/cli": "~9.1.7",
"@angular/compiler-cli": "~9.1.10",
"@angular/language-service": "~9.1.10",
"@types/jasmine": "~3.5.10",
"@types/jasminewd2": "~2.0.3",
"@types/node": "^14.0.11",
"codelyzer": "^5.1.2",
"@types/node": "^14.0.13",
"codelyzer": "^5.2.2",
"jasmine-core": "~3.5.0",
"karma": "^5.0.9",
"jasmine-spec-reporter": "~5.0.2",
"karma": "^5.0.9",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage-istanbul-reporter": "~3.0.2",
"karma-jasmine": "~3.1.1",
"karma-coverage-istanbul-reporter": "~3.0.3",
"karma-jasmine": "^3.3.1",
"karma-jasmine-html-reporter": "^1.5.4",
"prettier": "^2.0.5",
"protractor": "^7.0.0",

View File

@ -39,10 +39,11 @@
<div (clickOutside)="closeAccountCard()" class="icon-container">
<div class="avatar-wrapper dontcloseonclick" (click)="showAccount = !showAccount">
<div class="avatar-circle dontcloseonclick" [ngClass]="{'active': showAccount}">
<img class="avatar dontcloseonclick" *ngIf="componentCssClass == 'dark-theme'; else lighttheme"
src="../assets/images/account-circle-outline.png" />
<i *ngIf="componentCssClass == 'dark-theme'; else lighttheme"
class="avatar dontcloseonclick las la-user-circle"></i>
<ng-template #lighttheme>
<img class="avatar dontcloseonclick" src="../assets/images/account-circle-outline-dark.png" />
<i class="avatar las la-user-circle"></i>
</ng-template>
</div>
</div>
@ -58,31 +59,31 @@
<div class="list">
<a *ngIf="authService.authenticationChanged | async" class="nav-item" [routerLinkActive]="['active']"
[routerLinkActiveOptions]="{ exact: true }" [routerLink]="['/user/me']">
<mat-icon class="icon" svgIcon="mdi_account_circle_outline"></mat-icon>
<i class="icon las la-user-circle"></i>
<span class="label">{{ 'MENU.PERSONAL_INFO' | translate }}</span>
</a>
<a *ngIf="showOrgSection && org?.id" class="nav-item" [routerLinkActive]="['active']"
[routerLink]="[ '/orgs', org.id]">
<mat-icon class="icon">business</mat-icon>
<i class="icon las la-archway"></i>
<span class="label">{{org?.name ? org.name : 'MENU.ORGANIZATION' | translate}}</span>
</a>
<a *ngIf="showProjectSection" class="nav-item" [routerLinkActive]="['active']"
[routerLink]="[ '/projects']">
<mat-icon class="icon">folder_open</mat-icon>
<i class="icon las la-layer-group"></i>
<span class="label">{{ 'MENU.PROJECT' | translate }}</span>
</a>
<a *ngIf="showUserSection" class="nav-item" [routerLinkActive]="['active']" [routerLink]="[ '/users']"
[routerLinkActiveOptions]="{ exact: true }">
<mat-icon class="icon">people_outline</mat-icon>
<i class="icon las la-users"></i>
<span class="label">{{ 'MENU.USER' | translate }}</span>
</a>
<span class="fill-space"></span>
<a class="nav-item" (click)="authService.signout()">
<mat-icon class="icon" svgIcon="mdi_logout"></mat-icon>
<i class="icon las la-sign-out-alt"></i>
<span class="label">{{ 'MENU.LOGOUT' | translate }}</span>
</a>

View File

@ -54,33 +54,28 @@
.avatar-wrapper {
display: flex;
align-items: center;
color: white;
.avatar-circle {
height: 35px;
width: 35px;
height: 30px;
width: 30px;
font-size: 30px;
background-color: transparent;
border-radius: 50%;
animation: background-color .2s ease-in;
display: flex;
flex-direction: column;
align-items: center;
margin-left: .5rem;
.avatar {
display: block;
margin: auto auto;
height: 30px;
width: 30px;
line-height: 35px;
line-height: 30px;
font-size: 30px;
border-radius: 50%;
text-align: center;
fill: white;
* {
fill: white;
color: white;
}
}
&:hover, &.active {

View File

@ -170,11 +170,6 @@ export class AppComponent implements OnDestroy {
this.domSanitizer.bypassSecurityTrustResourceUrl('assets/mdi/account-cancel-outline.svg'),
);
this.matIconRegistry.addSvgIcon(
'mdi_logout',
this.domSanitizer.bypassSecurityTrustResourceUrl('assets/mdi/logout.svg'),
);
this.matIconRegistry.addSvgIcon(
'mdi_light_on',
this.domSanitizer.bypassSecurityTrustResourceUrl('assets/mdi/lightbulb-on-outline.svg'),

View File

@ -19,7 +19,7 @@
margin: 0;
font-weight: 400;
font-family: 'Rubik';
font-size: 1.2rem;
font-size: 18px;
// margin-top: .3rem;
}

View File

@ -10,4 +10,5 @@
<div class="sp-wrapper">
<mat-spinner *ngIf="loading | async" diameter="25"></mat-spinner>
</div>
<span class="err-container" *ngIf="errorMessage">{{errorMessage}}</span>
</div>

View File

@ -2,7 +2,6 @@
display: block;
margin-bottom: 1rem;
font-weight: 400;
color: #81868a;
margin-top: 1rem;
}
@ -37,4 +36,9 @@
display: flex;
justify-content: center;
}
.err-container {
font-size: 14px;
color: rgb(201,51,71);
}
}

View File

@ -1,7 +1,7 @@
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 { BehaviorSubject, from, Observable, of } from 'rxjs';
import { catchError, 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';
@ -19,6 +19,7 @@ export enum ChangeType {
export class ChangesComponent implements OnInit {
@Input() public changeType: ChangeType = ChangeType.USER;
@Input() public id: string = '';
public errorMessage: string = '';
// Source data
private _done: BehaviorSubject<any> = new BehaviorSubject(false);
@ -50,7 +51,6 @@ export class ChangesComponent implements OnInit {
break;
case ChangeType.ORG: first = this.mgmtUserService.OrgChanges(this.id, 10, 0);
break;
}
this.mapAndUpdate(first);
@ -100,6 +100,7 @@ export class ChangesComponent implements OnInit {
// Map snapshot with doc ref (needed for cursor)
return from(col).pipe(
tap((res: Changes) => {
console.log('more cahnge');
let values = res.toObject().changesList;
// If prepending, reverse the batch order
values = false ? values.reverse() : values;
@ -114,7 +115,14 @@ export class ChangesComponent implements OnInit {
this._done.next(true);
}
}),
take(1)).subscribe();
catchError(err => {
console.error(err);
this._loading.next(false);
this.errorMessage = err.message;
return of([]);
}),
take(1),
).subscribe();
}
public dateFromTimestamp(date: Timestamp.AsObject): any {

View File

@ -31,6 +31,7 @@ export class ProjectRolesDataSource extends DataSource<ProjectRole.AsObject> {
catchError(() => of([])),
finalize(() => this.loadingSubject.next(false)),
).subscribe(roles => {
console.log(roles);
this.rolesSubject.next(roles);
});
}

View File

@ -44,9 +44,9 @@
</td>
</ng-container>
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.ROLE.NAME' | translate }} </th>
<td mat-cell *matCellDef="let role"> {{role.name}} </td>
<ng-container matColumnDef="key">
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.ROLE.KEY' | translate }} </th>
<td mat-cell *matCellDef="let role"> {{role.key}} </td>
</ng-container>
<ng-container matColumnDef="displayname">
@ -63,6 +63,15 @@
</td>
</ng-container>
<ng-container matColumnDef="creationDate">
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.ROLE.CREATIONDATE' | translate }} </th>
<td mat-cell *matCellDef="let role">
<span>{{dateFromTimestamp(role.creationDate) | date: 'dd. MMM, HH:mm' }}</span>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>

View File

@ -2,6 +2,7 @@ import { SelectionModel } from '@angular/cdk/collections';
import { AfterViewInit, Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
import { MatPaginator } from '@angular/material/paginator';
import { MatTable } from '@angular/material/table';
import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb';
import { tap } from 'rxjs/operators';
import { ProjectRole } from 'src/app/proto/generated/management_pb';
import { ProjectService } from 'src/app/services/project.service';
@ -26,7 +27,7 @@ export class ProjectRolesComponent implements AfterViewInit, OnInit {
@Output() public changedSelection: EventEmitter<Array<ProjectRole.AsObject>> = new EventEmitter();
/** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */
public displayedColumns: string[] = ['select', 'name', 'displayname', 'group'];
public displayedColumns: string[] = ['select', 'key', 'displayname', 'group', 'creationDate'];
constructor(private projectService: ProjectService, private toast: ToastService) { }
@ -107,4 +108,9 @@ export class ProjectRolesComponent implements AfterViewInit, OnInit {
this.toast.showError(data.message);
});
}
public dateFromTimestamp(date: Timestamp.AsObject): any {
const ts: Date = new Date(date.seconds * 1000 + date.nanos / 1000);
return ts;
}
}

View File

@ -12,7 +12,6 @@ import {
SearchMethod,
} from 'src/app/proto/generated/management_pb';
import { ProjectService } from 'src/app/services/project.service';
import { ToastService } from 'src/app/services/toast.service';
@Component({
selector: 'app-search-project-autocomplete',
@ -33,7 +32,7 @@ export class SearchProjectAutocompleteComponent {
@ViewChild('auto') public matAutocomplete!: MatAutocomplete;
@Input() public singleOutput: boolean = false;
@Output() public selectionChanged: EventEmitter<Project.AsObject[] | Project.AsObject> = new EventEmitter();
constructor(private projectService: ProjectService, private toast: ToastService) {
constructor(private projectService: ProjectService) {
this.myControl.valueChanges
.pipe(
debounceTime(200),

View File

@ -107,6 +107,7 @@ export class AppCreateComponent implements OnInit, OnDestroy {
this.showSavedDialog(data.toObject());
})
.catch(data => {
console.error(data);
this.toast.showError(data.message);
});
}

View File

@ -8,16 +8,18 @@
<p class="desc">{{ 'APP.PAGES.DESCRIPTION' | translate }}</p>
</div>
<span *ngIf="errorMessage" class="err-container">{{errorMessage}}</span>
<app-card title="{{ 'APP.PAGES.DETAIL.TITLE' | translate }}" *ngIf="app">
<form [formGroup]="appNameForm" (ngSubmit)="saveOIDCApp()">
<div class="content">
<mat-button-toggle-group formControlName="state" class="toggle" (change)="changeState($event)">
<mat-button-toggle [value]="AppState.APPSTATE_INACTIVE" matTooltip="Deactivate Org">
<mat-icon svgIcon="mdi_light_off"></mat-icon>
<i class="las la-toggle-off"></i>
{{'APP.PAGES.DETAIL.STATE.'+AppState.APPSTATE_INACTIVE | translate}}
</mat-button-toggle>
<mat-button-toggle [value]="AppState.APPSTATE_ACTIVE" matTooltip="Activate Org">
<mat-icon svgIcon="mdi_light_on"></mat-icon>
<i class="las la-toggle-on"></i>
{{'APP.PAGES.DETAIL.STATE.'+AppState.APPSTATE_ACTIVE | translate}}
</mat-button-toggle>
</mat-button-toggle-group>

View File

@ -23,6 +23,11 @@
}
}
.err-container {
color: rgb(201,51,71);
font-size: 14px;
}
.card-actions {
button {
border-radius: .5rem;
@ -57,6 +62,10 @@
margin-bottom: 1rem;
margin-right: 1rem;
border-radius: .5rem;
i {
margin-right: 1rem;
}
}
}

View File

@ -33,6 +33,7 @@ enum RedirectType {
styleUrls: ['./app-detail.component.scss'],
})
export class AppDetailComponent implements OnInit, OnDestroy {
public errorMessage: string = '';
public selectable: boolean = false;
public removable: boolean = true;
public addOnBlur: boolean = true;
@ -103,25 +104,31 @@ export class AppDetailComponent implements OnInit, OnDestroy {
private async getData({ projectid, id }: Params): Promise<void> {
this.projectId = projectid;
this.app = (await this.projectService.GetApplicationById(projectid, id)).toObject();
this.appNameForm.patchValue(this.app);
if (this.app.state !== AppState.APPSTATE_ACTIVE) {
this.appNameForm.controls['name'].disable();
this.appForm.disable();
} else {
this.appNameForm.controls['name'].enable();
this.appForm.enable();
this.clientId?.disable();
}
if (this.app.oidcConfig?.redirectUrisList) {
this.redirectUrisList = this.app.oidcConfig.redirectUrisList;
}
if (this.app.oidcConfig?.postLogoutRedirectUrisList) {
this.postLogoutRedirectUrisList = this.app.oidcConfig.postLogoutRedirectUrisList;
}
if (this.app.oidcConfig) {
this.appForm.patchValue(this.app.oidcConfig);
}
this.projectService.GetApplicationById(projectid, id).then(app => {
this.app = app.toObject();
this.appNameForm.patchValue(this.app);
if (this.app.state !== AppState.APPSTATE_ACTIVE) {
this.appNameForm.controls['name'].disable();
this.appForm.disable();
} else {
this.appNameForm.controls['name'].enable();
this.appForm.enable();
this.clientId?.disable();
}
if (this.app.oidcConfig?.redirectUrisList) {
this.redirectUrisList = this.app.oidcConfig.redirectUrisList;
}
if (this.app.oidcConfig?.postLogoutRedirectUrisList) {
this.postLogoutRedirectUrisList = this.app.oidcConfig.postLogoutRedirectUrisList;
}
if (this.app.oidcConfig) {
this.appForm.patchValue(this.app.oidcConfig);
}
}).catch(error => {
console.error(error);
this.toast.showError(error.message);
this.errorMessage = error.message;
});
}
public changeState(event: MatButtonToggleChange): void {

View File

@ -13,22 +13,20 @@
<div class="container">
<div matTooltip="{{'ORG.PAGES.SELECTORGTOOLTIP' | translate}}" class="item card"
*ngFor="let org of orgList; index as i" (click)="selectOrg(org, $event)"
[ngClass]="{ selected: selection.isSelected(org) }">
[ngClass]="{ selected: selection.isSelected(org),active: activeOrg?.id === org?.id }">
<!-- <mat-icon matTooltip="select org" (click)="selection.toggle(org)" class="selection-icon">
check_circle</mat-icon> -->
<div class="text-part">
<span *ngIf="org?.changeDate" class="top">last modified on
<!-- <span *ngIf="org?.changeDate" class="top">last modified on
{{
dateFromTimestamp(org.changeDate) | date: 'EEE dd. MMM, HH:mm'
}}</span>
}}</span> -->
<span class="description">{{org.id}}</span>
<span class="name" *ngIf="org.name">{{ org.name }}</span>
<span class="name" *ngIf="!org.name">No Name</span>
<span class="description">{{org.id}}</span>
<span class="fill-space"></span>
<div class="icons">
<div class="current" *ngIf="activeOrg?.id === org?.id">
<span>{{'ORG.PAGES.ACTIVE' | translate}}</span></div>
</div>
</div>
<button [matMenuTriggerFor]="editMenu" class="edit-button" mat-icon-button>
@ -37,12 +35,12 @@
<mat-menu #editMenu="matMenu">
<ng-template matMenuContent>
<button (click)="selectOrg(org)" mat-menu-item>
<button (click)="routeToOrg(org)" mat-menu-item>
{{'ACTIONS.VIEW' | translate}}
</button>
<button (click)="selection.toggle(org)" mat-menu-item>
<!-- <button (click)="selection.toggle(org)" mat-menu-item>
{{'ACTIONS.INFO' | translate}}
</button>
</button> -->
</ng-template>
</mat-menu>
</div>

View File

@ -52,6 +52,10 @@ h1 {
box-sizing: border-box;
}
&.active {
border-color: #db4c69;
}
.selection-icon {
opacity: 0;
position: absolute;
@ -88,6 +92,7 @@ h1 {
.description {
font-size: 0.8rem;
margin-top: .5rem;
}
.created {
@ -120,6 +125,7 @@ h1 {
.current {
height: 10px;
font-size: 14px;
width: 10px;
border-radius: 50%;
background-color: rgb(144,212,210);

View File

@ -42,10 +42,14 @@ export class OrgGridComponent {
public selectOrg(item: Org.AsObject, event?: any): void {
if (event && !event.target.classList.contains('mat-icon')) {
this.authService.setActiveOrg(item);
this.router.navigate(['/orgs', item.id]);
this.routeToOrg(item);
}
}
public routeToOrg(item: Org.AsObject): void {
this.router.navigate(['/orgs', item.id]);
}
public dateFromTimestamp(date: Timestamp.AsObject): any {
const ts: Date = new Date(date.seconds * 1000 + date.nanos / 1000);
return ts;

View File

@ -31,6 +31,7 @@ export class OrgMembersDataSource extends DataSource<OrgMember.AsObject> {
catchError(() => of([])),
finalize(() => this.loadingSubject.next(false)),
).subscribe(members => {
console.log(members);
this.membersSubject.next(members);
});
}

View File

@ -3,7 +3,7 @@
<p class="top-desc">{{'ORG.POLICY.DESCRIPTION' | translate}}</p>
<div class="row-lyt">
<div class="p-item card">
<!-- <div class="p-item card">
<div class="avatar">
<mat-icon svgIcon="mdi_lock_reset"></mat-icon>
</div>
@ -38,7 +38,7 @@
<button [disabled]="!agePolicy" [routerLink]="[ 'policy', PolicyComponentType.AGE ]"
mat-raised-button>{{'ORG.POLICY.BTN_EDIT' | translate}}</button>
</div>
</div>
</div> -->
<div class="p-item card">
<div class="avatar">
<mat-icon svgIcon="mdi_textbox_password"></mat-icon>
@ -70,7 +70,7 @@
mat-raised-button>{{'ORG.POLICY.BTN_EDIT' | translate}}</button>
</div>
</div>
<div class="p-item card">
<!-- <div class="p-item card">
<div class="avatar">
<mat-icon svgIcon="mdi_lock_question"></mat-icon>
</div>
@ -98,5 +98,5 @@
<button [disabled]="!lockoutPolicy" [routerLink]="[ 'policy', PolicyComponentType.LOCKOUT ]"
mat-raised-button>{{'ORG.POLICY.BTN_EDIT' | translate}}</button>
</div>
</div>
</div> -->
</div>

View File

@ -32,8 +32,8 @@ export class PolicyGridComponent implements OnInit {
}
private getData(): void {
this.orgService.GetPasswordLockoutPolicy().then(data => this.lockoutPolicy = data.toObject()).catch(error => { });
this.orgService.GetPasswordAgePolicy().then(data => this.agePolicy = data.toObject()).catch(error => { });
// this.orgService.GetPasswordLockoutPolicy().then(data => this.lockoutPolicy = data.toObject()).catch(error => { });
// this.orgService.GetPasswordAgePolicy().then(data => this.agePolicy = data.toObject()).catch(error => { });
this.orgService.GetPasswordComplexityPolicy().then(data => this.complexityPolicy = data.toObject())
.catch(error => { });
}

View File

@ -15,6 +15,11 @@
<form @list (ngSubmit)="addRole()">
<div @animate *ngFor="let formGroup of formArray.controls; index as i" class="content">
<ng-container [formGroup]="formGroup">
<mat-form-field appearance="outline" class="formfield">
<mat-label>{{ 'PROJECT.ROLE.KEY' | translate }}</mat-label>
<input matInput formControlName="key" />
<!-- <mat-error *ngIf="name?.errors?.required">{{'ERRORS.REQUIRED' | translate}}</mat-error> -->
</mat-form-field>
<mat-form-field appearance="outline" class="formfield">
<mat-label>{{ 'PROJECT.ROLE.NAME' | translate }}</mat-label>
<input matInput formControlName="name" />

View File

@ -50,6 +50,7 @@ export class ProjectRoleCreateComponent implements OnInit, OnDestroy {
private fb: FormBuilder,
) {
this.formGroup = new FormGroup({
key: new FormControl(''),
name: new FormControl(''),
displayName: new FormControl(''),
group: new FormControl('', [Validators.required]),

View File

@ -2,7 +2,7 @@
<h3>{{'APP.LIST' | translate}}</h3>
<span class="fill-space"></span>
<button mat-icon-button (click)="closeView()">
<mat-icon matTooltip="show list view">list</mat-icon>
<i class="show list view las la-th-list"></i>
</button>
</div>
<div class="app-container">

View File

@ -18,6 +18,7 @@
padding-bottom: 2rem;
.app-wrap {
outline: none;
display: flex;
flex-direction: column;
align-items: center;

View File

@ -73,4 +73,8 @@
.pointer {
outline: none;
cursor: pointer;
}
}
tr {
outline: none;
}

View File

@ -5,15 +5,15 @@
<div class="img-list">
<ng-container *ngIf="totalResult < 10; else compact">
<ng-container *ngFor="let member of membersSubject | async">
<img (click)="showDetail()" *ngIf="member.imageURL; else render" class="avatar-img"
<!-- <img (click)="showDetail()" *ngIf="member.imageURL; else render" class="avatar-img"
[src]="member.imageURL" matTooltip="{{ member.email }} | {{member.rolesList?.join(' ')}}"
alt="editor avatar" />
<ng-template #render>
<div (click)="showDetail()" class="avatar-circle"
matTooltip="{{ member.email }} | {{member.rolesList?.join(' ')}}">
<mat-icon>face</mat-icon>
</div>
</ng-template>
<ng-template #render> -->
<div (click)="showDetail()" class="avatar-circle"
matTooltip="{{ member.email }} | {{member.rolesList?.join(' ')}}">
<mat-icon>face</mat-icon>
</div>
<!-- </ng-template> -->
</ng-container>
</ng-container>
<ng-template #compact>
@ -22,7 +22,7 @@
</div>
</ng-template>
<button class="add-img" (click)="openAddMember()"
[disabled]="project?.state !== ProjectState.ACTIVE_PROJECT" mat-icon-button
[disabled]="project?.state !== ProjectState.PROJECTSTATE_ACTIVE" mat-icon-button
aria-label="Edit contributors">
<mat-icon>add</mat-icon>
</button>

View File

@ -5,7 +5,6 @@
display: block;
margin-bottom: 1rem;
font-weight: 400;
color: #81868a;
}
.sub-header {

View File

@ -6,8 +6,8 @@ import { catchError, finalize, map } from 'rxjs/operators';
import { User } from 'src/app/proto/generated/auth_pb';
import {
GrantedProject,
ProjectMember,
ProjectMemberSearchResponse,
ProjectMemberView,
ProjectState,
ProjectType,
} from 'src/app/proto/generated/management_pb';
@ -26,10 +26,13 @@ import {
})
export class ProjectContributorsComponent implements OnInit {
@Input() public project!: GrantedProject.AsObject;
@Input() public projectType!: ProjectType;
@Input() public disabled: boolean = false;
public totalResult: number = 0;
public membersSubject: BehaviorSubject<ProjectMember.AsObject[]> = new BehaviorSubject<ProjectMember.AsObject[]>([]);
public membersSubject: BehaviorSubject<ProjectMemberView.AsObject[]>
= new BehaviorSubject<ProjectMemberView.AsObject[]>([]);
public ProjectState: any = ProjectState;
private loadingSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
@ -39,10 +42,11 @@ export class ProjectContributorsComponent implements OnInit {
private router: Router) { }
public ngOnInit(): void {
console.log(this.project);
const promise: Promise<ProjectMemberSearchResponse> | undefined =
this.project.type === ProjectType.PROJECTTYPE_OWNED ?
this.projectType === ProjectType.PROJECTTYPE_OWNED ?
this.projectService.SearchProjectMembers(this.project.id, 100, 0) :
this.project.type === ProjectType.PROJECTTYPE_GRANTED ?
this.projectType === ProjectType.PROJECTTYPE_GRANTED ?
this.projectService.SearchProjectGrantMembers(this.project.id, this.project.grantId, 100, 0) : undefined;
if (promise) {
from(promise).pipe(

View File

@ -43,7 +43,7 @@
<app-card *ngIf="!grid" title="{{ 'PROJECT.APP.TITLE' | translate }}">
<card-actions class="card-actions">
<button mat-icon-button (click)="grid = true">
<mat-icon matTooltip="show grid view">grid_on</mat-icon>
<i matTooltip="show grid view" class="las la-th-large"></i>
</button>
</card-actions>
<app-project-applications [disabled]="project?.state !== ProjectState.PROJECTSTATE_ACTIVE"
@ -81,7 +81,6 @@
</ng-template>
</div>
<metainfo class="side">
<div class="details">
<div class="row">
<span class="first">{{'PROJECT.TYPE.TITLE' | translate}}:</span>
@ -98,7 +97,8 @@
<mat-tab-group mat-stretch-tabs class="tab-group" disablePagination="true">
<mat-tab label="Details">
<app-project-contributors *ngIf="project"
[disabled]="project?.state !== ProjectState.PROJECTSTATE_ACTIVE" [project]="project">
[disabled]="project?.state !== ProjectState.PROJECTSTATE_ACTIVE" [projectType]="projectType"
[project]="project">
</app-project-contributors>
</mat-tab>
<mat-tab label="{{ 'CHANGES.PROJECT.TITLE' | translate }}" class="flex-col">

View File

@ -82,6 +82,7 @@ export class ProjectDetailComponent implements OnInit, OnDestroy {
this.grantId = grantId;
if (grantId) {
this.projectType = ProjectType.PROJECTTYPE_GRANTED;
// this.projectService.GetGrantedProjectGrantByID(id, this.grantId).then(proj => {
// this.projectGrant = proj.toObject();
// this.isZitadel$ = from(this.projectService.SearchApplications(this.project.id, 100, 0).then(appsResp => {
@ -93,6 +94,7 @@ export class ProjectDetailComponent implements OnInit, OnDestroy {
// this.toast.showError(error.message);
// });
} else {
this.projectType = ProjectType.PROJECTTYPE_OWNED;
this.projectService.GetProjectById(id).then(proj => {
this.project = proj.toObject();
// if (this.project.type !== ProjectType.PROJECTTYPE_SELF ||
@ -105,6 +107,7 @@ export class ProjectDetailComponent implements OnInit, OnDestroy {
.filter(app => app.oidcConfig?.clientId === this.grpcService.clientid).length > 0;
return ret;
})); // TODO: replace with prettier thing
this.isZitadel$.subscribe(isZita => console.log(`zitade: ${isZita}`));
}).catch(error => {
this.toast.showError(error.message);
});

View File

@ -7,7 +7,7 @@
</app-search-user-autocomplete>
<mat-form-field class="full-width">
<mat-label>{{ 'APP.OIDC.RESPONSE' | translate }}</mat-label>
<mat-label>{{ 'PROJECT.MEMBER.ROLES' | translate }}</mat-label>
<mat-select [(ngModel)]="roleKeyList" multiple>
<mat-option *ngFor="let key of data.roleKeysList" [value]="key">
{{ key }}

View File

@ -87,13 +87,15 @@
<span class="mem-title">Members</span>
<ng-template appHasRole
[appHasRole]="['project.grant.member.write:' + projectId, 'project.grant.member.write']">
<button [disabled]="disabled" mat-icon-button
<button [disabled]="disabled || element?.roleKeysList?.length === 0"
matTooltip="disabled or no roles defined" mat-icon-button
(click)="addProjectGrantMember(element)">
<mat-icon>add</mat-icon>
</button>
</ng-template>
</div>
<div class="mem-description">
<div class="mem-description"
*ngIf="selectedGrantMembers && selectedGrantMembers.length > 0">
<span *ngFor="let mem of selectedGrantMembers">{{mem.firstName}} {{mem.lastName}}
{{mem.email}} |
<span *ngFor="let role of mem.rolesList">{{role}} </span></span>

View File

@ -6,7 +6,7 @@ import { MatPaginator } from '@angular/material/paginator';
import { MatTable } from '@angular/material/table';
import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb';
import { tap } from 'rxjs/operators';
import { ProjectGrant, ProjectGrantMember } from 'src/app/proto/generated/management_pb';
import { ProjectGrant, ProjectMemberView } from 'src/app/proto/generated/management_pb';
import { ProjectService } from 'src/app/services/project.service';
import { ToastService } from 'src/app/services/toast.service';
@ -36,7 +36,7 @@ export class ProjectGrantsComponent implements OnInit, AfterViewInit {
public dataSource!: ProjectGrantsDataSource;
public selection: SelectionModel<ProjectGrant.AsObject> = new SelectionModel<ProjectGrant.AsObject>(true, []);
public expandedElement: ProjectGrant.AsObject | null = null;
public selectedGrantMembers: ProjectGrantMember.AsObject[] = [];
public selectedGrantMembers: ProjectMemberView.AsObject[] = [];
/** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */
public displayedColumns: string[] = ['select', 'grantedOrgName', 'grantedOrgDomain', 'creationDate', 'changeDate', 'roleNamesList'];
@ -97,6 +97,13 @@ export class ProjectGrantsComponent implements OnInit, AfterViewInit {
width: '400px',
});
console.log({
orgId: grant.grantedOrgId,
grantId: grant.id,
projectId: grant.projectId,
roleKeysList: grant.roleKeysList,
});
dialogRef.afterClosed().subscribe((dataToAdd: ProjectGrantMembersCreateDialogExportType) => {
if (dataToAdd) {
dataToAdd.userIds.forEach(userid => {

View File

@ -3,23 +3,23 @@
<ng-template appHasRole [appHasRole]="['project.write']">
<button (click)="deactivateProjects(selection.selected)" @animate
matTooltip="{{'PROJECT.TABLE.DEACTIVATE' | translate}}" class="left-button" mat-icon-button>
<mat-icon svgIcon="mdi_light_off"></mat-icon>
<i class="las la-toggle-off"></i>
</button>
<button @animate (click)="reactivateProjects(selection.selected)" class="left-button"
matTooltip="{{'PROJECT.TABLE.ACTIVATE' | translate}}" mat-icon-button>
<mat-icon svgIcon="mdi_light_on"></mat-icon>
<i class="las la-toggle-on"></i>
</button>
</ng-template>
</div>
<button [disabled]="selection.selected.length > 0" (click)="changedView.emit(true)" mat-icon-button>
<mat-icon matTooltip="show list view">list</mat-icon>
<i class="show list view las la-th-list"></i>
</button>
</div>
<div class="container">
<mat-progress-bar *ngIf="loading" class="spinner" color="accent" mode="indeterminate"></mat-progress-bar>
<div class="item card" *ngFor="let item of items; index as i" (click)="selectItem(item, $event)"
[ngClass]="{ selected: selection.isSelected(item), inactive: item.state !== ProjectState.ACTIVE_PROJECT}">
[ngClass]="{ selected: selection.isSelected(item), inactive: item.state !== ProjectState.PROJECTSTATE_ACTIVE}">
<mat-icon matTooltip="select item" (click)="selection.toggle(item)" class="selection-icon">
check_circle</mat-icon>
<div class="text-part">
@ -29,8 +29,8 @@
}}</span>
<span class="name" *ngIf="item.name">{{ item.name }}</span>
<span class="description" *ngIf="item.state">{{'PROJECT.STATE.'+item.state | translate}}</span>
<!-- <span class="description" *ngIf="item.type">{{'PROJECT.TYPE.TITLE' | translate}}:
{{'PROJECT.TYPE.'+item.type | translate}}</span> -->
<span class="description" *ngIf="item.type !== undefined">{{'PROJECT.TYPE.TITLE' | translate}}:
{{'PROJECT.TYPE.'+item.type | translate}}</span>
<span *ngIf="item.changeDate" class="created">created on
{{
dateFromTimestamp(item.creationDate) | date: 'EEE dd. MMM, HH:mm'
@ -52,8 +52,6 @@
<button (click)="selection.toggle(item)" mat-menu-item>
{{'ACTIONS.INFO' | translate}}
</button>
<button (click)="deleteProject(item)" mat-menu-item> {{'ACTIONS.DELETE' | translate}}</button>
</ng-template>
</mat-menu>
</div>

View File

@ -53,6 +53,10 @@
top: -12px;
left: -12px;
user-select: none;
&:hover {
color: white;
}
}
img {
@ -121,6 +125,10 @@
color: #81868a;
}
}
span {
margin: 2px 0;
}
}
.edit-button {

View File

@ -32,12 +32,12 @@ import { ToastService } from 'src/app/services/toast.service';
],
})
export class ProjectGridComponent {
@Input() items: Array<Project.AsObject> = [];
@Input() items: Array<GrantedProject.AsObject> = [];
@Output() newClicked: EventEmitter<boolean> = new EventEmitter();
@Output() changedView: EventEmitter<boolean> = new EventEmitter();
@Input() loading: boolean = false;
public selection: SelectionModel<Project.AsObject> = new SelectionModel<Project.AsObject>(true, []);
public selection: SelectionModel<GrantedProject.AsObject> = new SelectionModel<GrantedProject.AsObject>(true, []);
public selectedIndex: number = -1;
public showNewProject: boolean = false;
@ -48,41 +48,25 @@ export class ProjectGridComponent {
public selectItem(item: GrantedProject.AsObject, event?: any): void {
if (event && !event.target.classList.contains('mat-icon')) {
if (item.grantId) {
this.router.navigate(['/project-grant', `${item.id}:${item.grantId}`]);
this.router.navigate([item.id, '/grant', `${item.grantId}`]);
} else {
this.router.navigate(['/projects', item.id]);
}
} else if (!event) {
this.router.navigate(['/projects', item.id]);
if (item.grantId) {
this.router.navigate([item.id, '/grant', `${item.grantId}`]);
} else {
this.router.navigate(['/projects', item.id]);
}
}
}
public addItem(): void {
this.newClicked.emit(true);
}
public deleteProjects(selected: Project.AsObject[]): void {
// TODO: implement service
// Promise.all([selected.map(proj => {
// return this.projectService.DeleteProject(proj.id);
// })]).then(() => {
// this.toast.showInfo('Successful deleted all projects');
// }).catch(error => {
// this.toast.showError(error.message);
// });
}
public deleteProject(proj: Project.AsObject): void {
// TODO: implement service
// this.projectService.DeleteProject(proj.id).then(() => {
// this.toast.showInfo('Successful deleted Project');
// }).catch(error => {
// this.toast.showError(error.message);
// });
}
public dateFromTimestamp(date: Timestamp.AsObject): any {
const ts: Date = new Date(date.seconds * 1000 + date.nanos / 1000);
return ts;

View File

@ -8,7 +8,7 @@
<div *ngIf="!grid" class="view-toggle">
<button (click)="grid = true" mat-icon-button>
<mat-icon matTooltip="show grid view">grid_on</mat-icon>
<i matTooltip="show grid view" class="las la-th-large"></i>
</button>
</div>
<div *ngIf="!grid && projectList">
@ -83,7 +83,7 @@
<ng-container matColumnDef="type">
<th mat-header-cell *matHeaderCellDef> {{ 'PROJECT.TABLE.TYPE' | translate }} </th>
<td mat-cell *matCellDef="let project">
<span *ngIf="project.type">{{'PROJECT.TYPE.'+project.type | translate}}</span>
<span *ngIf="project.type !== undefined">{{'PROJECT.TYPE.'+project.type | translate}}</span>
</td>
</ng-container>

View File

@ -100,4 +100,8 @@ h1 {
max-width: 50px;
}
}
}
tr {
outline: none;
}

View File

@ -1,6 +1,6 @@
import { animate, animateChild, query, stagger, style, transition, trigger } from '@angular/animations';
import { SelectionModel } from '@angular/cdk/collections';
import { Component, OnInit } from '@angular/core';
import { Component, OnDestroy, OnInit } from '@angular/core';
import { PageEvent } from '@angular/material/paginator';
import { MatTableDataSource } from '@angular/material/table';
import { ActivatedRoute, Router } from '@angular/router';
@ -35,7 +35,7 @@ import { ToastService } from 'src/app/services/toast.service';
]),
],
})
export class ProjectListComponent implements OnInit {
export class ProjectListComponent implements OnInit, OnDestroy {
public totalResult: number = 0;
public dataSource: MatTableDataSource<GrantedProject.AsObject> = new MatTableDataSource<GrantedProject.AsObject>();
public projectList: GrantedProject.AsObject[] = [];
@ -58,6 +58,10 @@ export class ProjectListComponent implements OnInit {
this.subscription = this.route.params.subscribe(() => this.getData(10, 0));
}
public ngOnDestroy(): void {
this.subscription?.unsubscribe();
}
public isAllSelected(): boolean {
const numSelected = this.selection.selected.length;
const numRows = this.dataSource.data.length;

View File

@ -1,6 +1,7 @@
import { DataSource } from '@angular/cdk/collections';
import { BehaviorSubject, Observable } from 'rxjs';
import { Project, ProjectMember } from 'src/app/proto/generated/management_pb';
import { BehaviorSubject, from, Observable, of } from 'rxjs';
import { catchError, finalize, map } from 'rxjs/operators';
import { Project, ProjectMember, ProjectMemberSearchResponse, ProjectType } from 'src/app/proto/generated/management_pb';
import { ProjectService } from 'src/app/services/project.service';
/**
@ -18,29 +19,32 @@ export class ProjectMembersDataSource extends DataSource<ProjectMember.AsObject>
super();
}
public loadMembers(project: Project.AsObject, pageIndex: number, pageSize: number, sortDirection?: string): void {
public loadMembers(project: Project.AsObject,
projectType: ProjectType,
pageIndex: number, pageSize: number, grantId?: string, sortDirection?: string): void {
const offset = pageIndex * pageSize;
this.loadingSubject.next(true);
// TODO
// const promise: Promise<ProjectMemberSearchResponse> | undefined =
// project.type === ProjectType.PROJECTTYPE_OWNED ?
// this.projectService.SearchProjectMembers(project.id, pageSize, offset) :
// project.type === ProjectType.PROJECTTYPE_GRANTED ?
// this.projectService.SearchProjectGrantMembers(project.id,
// project.grantId, pageSize, offset) : 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);
// });
// }
const promise: Promise<ProjectMemberSearchResponse> | undefined =
projectType === ProjectType.PROJECTTYPE_OWNED ?
this.projectService.SearchProjectMembers(project.id, pageSize, offset) :
projectType === ProjectType.PROJECTTYPE_GRANTED && grantId ?
this.projectService.SearchProjectGrantMembers(project.id,
grantId, pageSize, offset) : undefined;
if (promise) {
from(promise).pipe(
map(resp => {
this.totalResult = resp.toObject().totalResult;
console.log(this.totalResult);
return resp.toObject().resultList;
}),
catchError(() => of([])),
finalize(() => this.loadingSubject.next(false)),
).subscribe(members => {
this.membersSubject.next(members);
});
}
}

View File

@ -1,11 +1,10 @@
import { SelectionModel } from '@angular/cdk/collections';
import { AfterViewInit, Component, ViewChild } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { MatPaginator } from '@angular/material/paginator';
import { MatTable } from '@angular/material/table';
import { ActivatedRoute } from '@angular/router';
import { tap } from 'rxjs/operators';
import { Project, ProjectMember } from 'src/app/proto/generated/management_pb';
import { Project, ProjectMember, ProjectType } from 'src/app/proto/generated/management_pb';
import { ProjectService } from 'src/app/services/project.service';
import { ToastService } from 'src/app/services/toast.service';
@ -18,6 +17,7 @@ import { ProjectMembersDataSource } from './project-members-datasource';
})
export class ProjectMembersComponent implements AfterViewInit {
public project!: Project.AsObject;
public projectType: ProjectType = ProjectType.PROJECTTYPE_OWNED;
public disabled: boolean = false;
@ViewChild(MatPaginator) public paginator!: MatPaginator;
@ViewChild(MatTable) public table!: MatTable<ProjectMember.AsObject>;
@ -28,14 +28,14 @@ export class ProjectMembersComponent implements AfterViewInit {
public displayedColumns: string[] = ['select', 'firstname', 'lastname', 'username', 'email', 'roles'];
constructor(private projectService: ProjectService,
private dialog: MatDialog,
private toast: ToastService,
private route: ActivatedRoute) {
this.route.params.subscribe(params => {
this.projectService.GetProjectById(params.projectid).then(project => {
this.project = project.toObject();
console.log(this.project);
this.dataSource = new ProjectMembersDataSource(this.projectService);
this.dataSource.loadMembers(this.project, 0, 25, 'asc');
this.dataSource.loadMembers(this.project, this.projectType, 0, 25, 'asc');
});
});
}
@ -52,6 +52,7 @@ export class ProjectMembersComponent implements AfterViewInit {
private loadMembersPage(): void {
this.dataSource.loadMembers(
this.project,
this.projectType,
this.paginator.pageIndex,
this.paginator.pageSize,
);

View File

@ -15,6 +15,7 @@ h1 {
.col {
display: flex;
flex-direction: column;
.desc {
font-size: .8rem;
color: #81868a;
@ -68,3 +69,7 @@ h1 {
}
}
}
tr {
outline: none;
}

View File

@ -87,6 +87,7 @@
},
"DATA": {
"STATE": "Status",
"STATE0": "unbekannt",
"STATE1": "aktiv",
"STATE2": "inaktiv",
"STATE3": "gelöscht",
@ -325,6 +326,7 @@
"NAME": "Name"
},
"ROLE": {
"KEY":"Key",
"TITLE": "Roles",
"DESCRIPTION":"Definieren Sie Rollen, die Sie bei der Erstellung eines Projekt-Grants vergeben können",
"NAME": "Name",
@ -333,7 +335,8 @@
"ACTIONS": "Aktion",
"ADDTITLE": "Rolle erstellen",
"ADDDESCRIPTION": "Geben Sie die Daten für die zu erstellende Rolle ein!",
"DELETE":"Rolle löschen"
"DELETE":"Rolle löschen",
"CREATIONDATE":"Erstelldatum"
},
"TABLE": {
"TOTAL": "Einträge gesamt:",
@ -344,7 +347,7 @@
"ORGNAME":"Org Name",
"ORGDOMAIN":"Org Domain",
"STATE":"Status",
"Type":"Typ",
"TYPE":"Typ",
"CREATIONDATE":"Erstelldatum",
"CHANGEDATE":"Letzte Änderung"
}

View File

@ -87,6 +87,7 @@
},
"DATA": {
"STATE": "Status",
"STATE0": "unknown",
"STATE1": "active",
"STATE2": "inactive",
"STATE3": "deleted",
@ -325,6 +326,7 @@
"NAME": "Name"
},
"ROLE": {
"KEY":"Key",
"TITLE": "Roles",
"DESCRIPTION":"Define some roles which can be used to create project-grants",
"NAME": "Name",
@ -333,7 +335,8 @@
"ACTIONS": "Actions",
"ADDTITLE": "Create role",
"ADDDESCRIPTION": "Enter the data for the new role!",
"DELETE":"Delete Role"
"DELETE":"Delete Role",
"CREATIONDATE":"Created"
},
"TABLE": {
"TOTAL": "Entries total:",
@ -344,7 +347,7 @@
"ORGNAME":"Org Name",
"ORGDOMAIN":"Org Domain",
"STATE":"Status",
"Type":"Type",
"TYPE":"Type",
"CREATIONDATE":"Created At",
"CHANGEDATE":"Last Modified"
}

View File

@ -1 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M7.07,18.28C7.5,17.38 10.12,16.5 12,16.5C13.88,16.5 16.5,17.38 16.93,18.28C15.57,19.36 13.86,20 12,20C10.14,20 8.43,19.36 7.07,18.28M18.36,16.83C16.93,15.09 13.46,14.5 12,14.5C10.54,14.5 7.07,15.09 5.64,16.83C4.62,15.5 4,13.82 4,12C4,7.59 7.59,4 12,4C16.41,4 20,7.59 20,12C20,13.82 19.38,15.5 18.36,16.83M12,6C10.06,6 8.5,7.56 8.5,9.5C8.5,11.44 10.06,13 12,13C13.94,13 15.5,11.44 15.5,9.5C15.5,7.56 13.94,6 12,6M12,11A1.5,1.5 0 0,1 10.5,9.5A1.5,1.5 0 0,1 12,8A1.5,1.5 0 0,1 13.5,9.5A1.5,1.5 0 0,1 12,11Z" /></svg>

Before

Width:  |  Height:  |  Size: 873 B

View File

@ -1 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M16,17V14H9V10H16V7L21,12L16,17M14,2A2,2 0 0,1 16,4V6H14V4H5V20H14V18H16V20A2,2 0 0,1 14,22H5A2,2 0 0,1 3,20V4A2,2 0 0,1 5,2H14Z" /></svg>

Before

Width:  |  Height:  |  Size: 423 B

View File

@ -11,6 +11,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500&amp;display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<link href="https://fonts.googleapis.com/css?family=Rubik&display=swap" rel="stylesheet">
<link rel="stylesheet"
href="https://maxst.icons8.com/vue-static/landings/line-awesome/line-awesome/1.3.0/css/line-awesome.min.css">
<link rel="manifest" href="manifest.webmanifest">
<meta name="theme-color" content="#e6768b">
</head>

View File

@ -207,3 +207,7 @@ body {
border-radius: 0.5rem;
background-color: #212224;
}
i {
font-size: 1.5rem;
}