fix(console): some imperfections, delete memberships from detail, role delete from table, app state (#1551)

* app state, roles, remove memberships, feature detail

* chore(deps): bump tslib from 2.0.3 to 2.2.0 in /console (#1549)

* fix: pass necessary webauthn data through events (#1541)

* fix: pass necessary webauthn data through events (#1544)

* docs: update readme (#1460) (#1545)

* Update readme

* Apply suggestions from code review but features

Co-authored-by: Florian Forster <florian@caos.ch>

* Update README.md

Co-authored-by: Florian Forster <florian@caos.ch>

Co-authored-by: Maximilian Panne <maximilian.panne@gmail.com>
Co-authored-by: Florian Forster <florian@caos.ch>
(cherry picked from commit 5c5b13cf84aca3641ec8fd43036c7ec25479d598)

Co-authored-by: mffap <mpa@caos.ch>

* chore(deps): bump tslib from 2.0.3 to 2.2.0 in /console

Bumps [tslib](https://github.com/Microsoft/tslib) from 2.0.3 to 2.2.0.
- [Release notes](https://github.com/Microsoft/tslib/releases)
- [Commits](https://github.com/Microsoft/tslib/compare/2.0.3...2.2.0)

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

Co-authored-by: Livio Amstutz <livio.a@gmail.com>
Co-authored-by: mffap <mpa@caos.ch>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps-dev): bump @angular/language-service from 11.2.0 to 11.2.8 in /console (#1521)

* fix: pass necessary webauthn data through events (#1541)

* fix: pass necessary webauthn data through events (#1544)

* docs: update readme (#1460) (#1545)

* Update readme

* Apply suggestions from code review but features

Co-authored-by: Florian Forster <florian@caos.ch>

* Update README.md

Co-authored-by: Florian Forster <florian@caos.ch>

Co-authored-by: Maximilian Panne <maximilian.panne@gmail.com>
Co-authored-by: Florian Forster <florian@caos.ch>
(cherry picked from commit 5c5b13cf84aca3641ec8fd43036c7ec25479d598)

Co-authored-by: mffap <mpa@caos.ch>

* chore(deps-dev): bump @angular/language-service in /console

Bumps [@angular/language-service](https://github.com/angular/angular/tree/HEAD/packages/language-service) from 11.2.0 to 11.2.8.
- [Release notes](https://github.com/angular/angular/releases)
- [Changelog](https://github.com/angular/angular/blob/11.2.8/CHANGELOG.md)
- [Commits](https://github.com/angular/angular/commits/11.2.8/packages/language-service)

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

Co-authored-by: Livio Amstutz <livio.a@gmail.com>
Co-authored-by: mffap <mpa@caos.ch>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps-dev): bump @angular-devkit/build-angular from 0.1102.1 to 0.1102.7 in /console (#1520)

* fix: pass necessary webauthn data through events (#1541)

* fix: pass necessary webauthn data through events (#1544)

* docs: update readme (#1460) (#1545)

* Update readme

* Apply suggestions from code review but features

Co-authored-by: Florian Forster <florian@caos.ch>

* Update README.md

Co-authored-by: Florian Forster <florian@caos.ch>

Co-authored-by: Maximilian Panne <maximilian.panne@gmail.com>
Co-authored-by: Florian Forster <florian@caos.ch>
(cherry picked from commit 5c5b13cf84aca3641ec8fd43036c7ec25479d598)

Co-authored-by: mffap <mpa@caos.ch>

* chore(deps-dev): bump @angular-devkit/build-angular in /console

Bumps [@angular-devkit/build-angular](https://github.com/angular/angular-cli) from 0.1102.1 to 0.1102.7.
- [Release notes](https://github.com/angular/angular-cli/releases)
- [Commits](https://github.com/angular/angular-cli/commits)

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

Co-authored-by: Livio Amstutz <livio.a@gmail.com>
Co-authored-by: mffap <mpa@caos.ch>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps-dev): bump karma from 6.1.1 to 6.3.2 in /console (#1519)

* fix: pass necessary webauthn data through events (#1541)

* fix: pass necessary webauthn data through events (#1544)

* docs: update readme (#1460) (#1545)

* Update readme

* Apply suggestions from code review but features

Co-authored-by: Florian Forster <florian@caos.ch>

* Update README.md

Co-authored-by: Florian Forster <florian@caos.ch>

Co-authored-by: Maximilian Panne <maximilian.panne@gmail.com>
Co-authored-by: Florian Forster <florian@caos.ch>
(cherry picked from commit 5c5b13cf84aca3641ec8fd43036c7ec25479d598)

Co-authored-by: mffap <mpa@caos.ch>

* chore(deps-dev): bump karma from 6.1.1 to 6.3.2 in /console

Bumps [karma](https://github.com/karma-runner/karma) from 6.1.1 to 6.3.2.
- [Release notes](https://github.com/karma-runner/karma/releases)
- [Changelog](https://github.com/karma-runner/karma/blob/master/CHANGELOG.md)
- [Commits](https://github.com/karma-runner/karma/compare/v6.1.1...v6.3.2)

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

Co-authored-by: Livio Amstutz <livio.a@gmail.com>
Co-authored-by: mffap <mpa@caos.ch>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): bump google-protobuf from 3.13.0 to 3.15.7 in /console (#1518)

* fix: pass necessary webauthn data through events (#1541)

* fix: pass necessary webauthn data through events (#1544)

* docs: update readme (#1460) (#1545)

* Update readme

* Apply suggestions from code review but features

Co-authored-by: Florian Forster <florian@caos.ch>

* Update README.md

Co-authored-by: Florian Forster <florian@caos.ch>

Co-authored-by: Maximilian Panne <maximilian.panne@gmail.com>
Co-authored-by: Florian Forster <florian@caos.ch>
(cherry picked from commit 5c5b13cf84aca3641ec8fd43036c7ec25479d598)

Co-authored-by: mffap <mpa@caos.ch>

* chore(deps): bump google-protobuf from 3.13.0 to 3.15.7 in /console

Bumps [google-protobuf](https://github.com/protocolbuffers/protobuf) from 3.13.0 to 3.15.7.
- [Release notes](https://github.com/protocolbuffers/protobuf/releases)
- [Changelog](https://github.com/protocolbuffers/protobuf/blob/master/generate_changelog.py)
- [Commits](https://github.com/protocolbuffers/protobuf/compare/v3.13.0...v3.15.7)

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

Co-authored-by: Livio Amstutz <livio.a@gmail.com>
Co-authored-by: mffap <mpa@caos.ch>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps-dev): bump @angular/cli from 11.2.0 to 11.2.7 in /console (#1517)

* fix: pass necessary webauthn data through events (#1541)

* fix: pass necessary webauthn data through events (#1544)

* docs: update readme (#1460) (#1545)

* Update readme

* Apply suggestions from code review but features

Co-authored-by: Florian Forster <florian@caos.ch>

* Update README.md

Co-authored-by: Florian Forster <florian@caos.ch>

Co-authored-by: Maximilian Panne <maximilian.panne@gmail.com>
Co-authored-by: Florian Forster <florian@caos.ch>
(cherry picked from commit 5c5b13cf84aca3641ec8fd43036c7ec25479d598)

Co-authored-by: mffap <mpa@caos.ch>

* chore(deps-dev): bump @angular/cli from 11.2.0 to 11.2.7 in /console

Bumps [@angular/cli](https://github.com/angular/angular-cli) from 11.2.0 to 11.2.7.
- [Release notes](https://github.com/angular/angular-cli/releases)
- [Commits](https://github.com/angular/angular-cli/compare/v11.2.0...v11.2.7)

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

Co-authored-by: Livio Amstutz <livio.a@gmail.com>
Co-authored-by: mffap <mpa@caos.ch>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Max Peintner <max@caos.ch>

* chore(deps-dev): bump @types/node from 14.14.28 to 14.14.37 in /console (#1489)

* fix: pass necessary webauthn data through events (#1541)

* fix: pass necessary webauthn data through events (#1544)

* docs: update readme (#1460) (#1545)

* Update readme

* Apply suggestions from code review but features

Co-authored-by: Florian Forster <florian@caos.ch>

* Update README.md

Co-authored-by: Florian Forster <florian@caos.ch>

Co-authored-by: Maximilian Panne <maximilian.panne@gmail.com>
Co-authored-by: Florian Forster <florian@caos.ch>
(cherry picked from commit 5c5b13cf84aca3641ec8fd43036c7ec25479d598)

Co-authored-by: mffap <mpa@caos.ch>

* chore(deps-dev): bump @types/node from 14.14.28 to 14.14.37 in /console

Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 14.14.28 to 14.14.37.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

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

Co-authored-by: Livio Amstutz <livio.a@gmail.com>
Co-authored-by: mffap <mpa@caos.ch>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Livio Amstutz <livio.a@gmail.com>
Co-authored-by: mffap <mpa@caos.ch>
This commit is contained in:
Max Peintner 2021-04-07 16:46:35 +02:00 committed by GitHub
parent 3487a7a713
commit e9a457ddf5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 1194 additions and 1130 deletions

2041
console/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -32,7 +32,7 @@
"cors": "^2.8.5",
"file-saver": "^2.0.5",
"google-proto-files": "^2.4.0",
"google-protobuf": "^3.13.0",
"google-protobuf": "^3.15.7",
"grpc": "^1.24.5",
"grpc-web": "^1.2.1",
"libphonenumber-js": "^1.9.13",
@ -40,22 +40,22 @@
"ngx-quicklink": "^0.2.6",
"rxjs": "~6.6.3",
"ts-protoc-gen": "^0.14.0",
"tslib": "^2.0.0",
"tslib": "^2.2.0",
"uuid": "^8.3.2",
"zone.js": "~0.11.3"
},
"devDependencies": {
"@angular/cli": "~11.2.0",
"@angular-devkit/build-angular": "~0.1102.1",
"@angular/cli": "~11.2.7",
"@angular-devkit/build-angular": "~0.1102.7",
"@angular/compiler-cli": "~11.0.0",
"@types/jasmine": "~3.6.3",
"@angular/language-service": "~11.2.0",
"@angular/language-service": "~11.2.8",
"@types/jasminewd2": "~2.0.3",
"@types/node": "^14.14.28",
"@types/node": "^14.14.37",
"codelyzer": "^6.0.0",
"jasmine-core": "~3.6.0",
"jasmine-spec-reporter": "~6.0.0",
"karma": "~6.1.1",
"karma": "~6.3.2",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage-istanbul-reporter": "~3.0.2",
"karma-jasmine": "~4.0.0",

View File

@ -31,17 +31,6 @@
dayelement.dates[j] | timestampToDate | localizedDate: 'HH:mm'
}}</span>
</span>
<!-- <span *ngIf="histindex === 0 && i === 0 && j === 0">
<span>current file</span>
<span class="block">{{
dayelement.dates[j] | timestampToDate | localizedDate: 'HH:mm'
}}</span>
</span> -->
<!-- <button mat-icon-button disabled>
<mat-icon class="icon red" color="warn">error_outline
</mat-icon>
</button> -->
</div>
</div>
</div>

View File

@ -129,14 +129,15 @@
display: flex;
justify-content: center;
}
}
.end-container {
font-size: 12px;
color: var(--grey);
font-size: 14px;
margin: 1rem 0;
color: var(--grey);
}
.end-container {
font-size: 12px;
color: var(--grey);
font-size: 14px;
margin: 1rem 0 1rem 0;
display: block;
color: var(--grey);
}
}
}

View File

@ -1,5 +1,5 @@
<app-detail-layout [backRouterLink]="[ serviceType === FeatureServiceType.ADMIN ? '/iam/policies' : '/org']"
[title]="'FEATURES.TITLE' | translate" [description]="'FEATURES.DESCRIPTION' | translate">
[title]="'ZITADEL ' +features?.tier.name + ' ' + ('FEATURES.TITLE' | translate)" [description]="'FEATURES.DESCRIPTION' | translate">
<p class="default" *ngIf="isDefault"> {{'POLICY.DEFAULTLABEL' | translate}}</p>
<ng-template appHasRole [appHasRole]="['iam.features.delete']">
@ -14,7 +14,7 @@
<div class="row">
<span class="left-desc">{{'FEATURES.DATA.AUDITLOGRETENTION' | translate}}</span>
<span class="fill-space"></span>
<span>{{features.auditLogRetention | json }}</span>
<span>{{features.auditLogRetention | timestampToRetention }} {{'FEATURES.RETENTIONHOURS' | translate}}</span>
</div>
<div class="row">
<span class="left-desc">{{'FEATURES.DATA.LOGINPOLICYUSERNAMELOGIN' | translate}}</span>

View File

@ -10,6 +10,9 @@ import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
import { DetailLayoutModule } from 'src/app/modules/detail-layout/detail-layout.module';
import { InputModule } from 'src/app/modules/input/input.module';
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module';
import {
TimestampToRetentionPipeModule,
} from 'src/app/pipes/timestamp-to-retention-pipe/timestamp-to-retention-pipe.module';
import { InfoSectionModule } from '../info-section/info-section.module';
import { FeaturesRoutingModule } from './features-routing.module';
@ -34,6 +37,7 @@ import { FeaturesComponent } from './features.component';
InfoSectionModule,
TranslateModule,
DetailLayoutModule,
TimestampToRetentionPipeModule,
],
exports: [
FeaturesComponent,

View File

@ -1,13 +1,7 @@
<app-refresh-table *ngIf="projectId" (refreshed)="refreshPage()" [dataSize]="dataSource?.totalResult"
[emitRefreshOnPreviousRoutes]="['/projects/'+projectId+'/roles/create']" [selection]="selection"
[loading]="dataSource?.loading$ | async" [timestamp]="dataSource?.viewTimestamp">
<ng-template appHasRole [appHasRole]="['project.role.delete', 'project.role.delete:' + projectId]" actions>
<button color="warn" class="icon-button" [disabled]="disabled"
matTooltip="{{'PROJECT.ROLE.DELETE' | translate}}" (click)="deleteSelectedRoles()" mat-icon-button
*ngIf="selection.hasValue() && actionsVisible">
<i class="las la-trash"></i>
</button>
</ng-template>
<ng-template appHasRole [appHasRole]="['project.role.write:' + projectId, 'project.role.write']" actions>
<a *ngIf="actionsVisible" [disabled]="disabled" [routerLink]="[ '/projects', projectId, 'roles', 'create']"
color="primary" mat-raised-button>
@ -57,6 +51,18 @@
</td>
</ng-container>
<ng-container matColumnDef="actions" stickyEnd>
<th mat-header-cell *matHeaderCellDef></th>
<td mat-cell *matCellDef="let role">
<button
[disabled]="disabled || (['project.role.delete', 'project.role.delete:' + projectId] | hasRole | async) == false"
mat-icon-button color="warn" matTooltip="{{'ACTIONS.DELETE' | translate}}"
(click)="deleteRole(role)">
<i class="las la-trash"></i>
</button>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr class="highlight" mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>

View File

@ -41,3 +41,18 @@
outline: none;
cursor: pointer;
}
tr {
outline: none;
button {
visibility: hidden;
}
&:hover {
button {
visibility: visible;
}
}
}

View File

@ -28,7 +28,7 @@ export class ProjectRolesComponent implements AfterViewInit, OnInit {
@Output() public changedSelection: EventEmitter<Array<Role.AsObject>> = new EventEmitter();
/** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */
public displayedColumns: string[] = ['select', 'key', 'displayname', 'group', 'creationDate'];
public displayedColumns: string[] = ['select', 'key', 'displayname', 'group', 'creationDate', 'actions'];
constructor(private mgmtService: ManagementService, private toast: ToastService, private dialog: MatDialog) {
this.dataSource = new ProjectRolesDataSource(this.mgmtService);
@ -76,25 +76,16 @@ export class ProjectRolesComponent implements AfterViewInit, OnInit {
this.dataSource.rolesSubject.value.forEach((row: Role.AsObject) => this.selection.select(row));
}
public deleteSelectedRoles(): Promise<any> {
const oldState = this.dataSource.rolesSubject.value;
const indexes = this.selection.selected.map(sel => {
return oldState.findIndex(iter => iter.key === sel.key);
});
public deleteRole(role: Role.AsObject): Promise<any> {
const index = this.dataSource.rolesSubject.value.findIndex(iter => iter.key === role.key);
return Promise.all(this.selection.selected.map(role => {
return this.mgmtService.removeProjectRole(this.projectId, role.key);
})).then(() => {
return this.mgmtService.removeProjectRole(this.projectId, role.key).then(() => {
this.toast.showInfo('PROJECT.TOAST.ROLEREMOVED', true);
indexes.forEach(index => {
if (index > -1) {
oldState.splice(index, 1);
this.dataSource.rolesSubject.next(this.dataSource.rolesSubject.value);
}
});
this.selection.clear();
}).catch(error => {
this.toast.showError(error);
if (index > -1) {
this.dataSource.rolesSubject.value.splice(index, 1);
this.dataSource.rolesSubject.next(this.dataSource.rolesSubject.value);
}
});
}

View File

@ -43,8 +43,6 @@
font-size: 16px;
margin-bottom: .5rem;
}
}
.fill-space {

View File

@ -13,7 +13,7 @@
<i matTooltip="verified" *ngIf="domain.isVerified" class="verified las la-check-circle"></i>
<i matTooltip="primary" *ngIf="domain.isPrimary" class="primary las la-star"></i>
<a *ngIf="!domain.isPrimary && (canwrite$ | async)" class="primaryset"
<a *ngIf="domain.isVerified && !domain.isPrimary && (canwrite$ | async)" class="primaryset"
(click)="setPrimary(domain)">{{'ORG.DOMAINS.SETPRIMARY' | translate}}</a>
<span class="fill-space"></span>

View File

@ -12,14 +12,26 @@
</div>
<ng-container *ngIf="isZitadel === false">
<ng-template appHasRole [appHasRole]="['project.app.write:'+projectId, 'project.app.write']">
<button *ngIf="!editState" matTooltip="{{'ACTIONS.EDIT' | translate}}" mat-icon-button
(click)="editState = !editState" aria-label="edit app name">
(click)="editState = !editState" aria-label="edit app name">
<i class="las la-edit"></i>
</button>
<button *ngIf="editState" (click)="saveApp()" [disabled]="appNameForm.invalid || name?.disabled"
mat-icon-button>
<i class="las la-save"></i>
</button>
<button class="state-button" mat-stroked-button color="warn"
*ngIf="app.state !== undefined && app?.state !== AppState.APP_STATE_INACTIVE"
(click)="changeState(AppState.APP_STATE_INACTIVE)">
{{'ACTIONS.DEACTIVATE' | translate}}
</button>
<button class="state-button" mat-stroked-button
*ngIf="app.state !== undefined && app?.state !== AppState.APP_STATE_ACTIVE"
(click)="changeState(AppState.APP_STATE_ACTIVE)">
{{'ACTIONS.REACTIVATE' | translate}}
</button>
</ng-template>
<ng-template appHasRole [appHasRole]="['project.app.delete:'+projectId, 'project.app.delete']">
@ -38,17 +50,6 @@
<form *ngIf="app && editState" [formGroup]="appNameForm">
<div class="name-content">
<mat-button-toggle-group formControlName="state" class="toggle" (change)="changeState($event)">
<mat-button-toggle [value]="AppState.APP_STATE_INACTIVE"
matTooltip="{{ 'ACTIONS.DEACTIVATE' | translate}}">
{{'APP.PAGES.DETAIL.STATE.'+AppState.APP_STATE_INACTIVE | translate}}
</mat-button-toggle>
<mat-button-toggle [value]="AppState.APPSTATE_ACTIVE"
matTooltip="{{ 'ACTIONS.REACTIVATE' | translate}}">
{{'APP.PAGES.DETAIL.STATE.'+AppState.APP_STATE_ACTIVE | translate}}
</mat-button-toggle>
</mat-button-toggle-group>
<cnsl-form-field class="name-field">
<cnsl-label>{{ 'APP.NAME' | translate }}</cnsl-label>
<input cnslInput formControlName="name" />

View File

@ -37,6 +37,10 @@
font-size: 14px;
color: rgb(201, 51, 71);
}
.state-button {
margin: 0 .5rem;
}
}
.err-container {

View File

@ -3,7 +3,6 @@ import { Location } from '@angular/common';
import { HttpClient } from '@angular/common/http';
import { Component, OnDestroy, OnInit } from '@angular/core';
import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms';
import { MatButtonToggleChange } from '@angular/material/button-toggle';
import { MatDialog } from '@angular/material/dialog';
import { MatSnackBar } from '@angular/material/snack-bar';
import { ActivatedRoute, Params, Router } from '@angular/router';
@ -382,15 +381,17 @@ export class AppDetailComponent implements OnInit, OnDestroy {
});
}
public changeState(event: MatButtonToggleChange): void {
if (event.value === AppState.APP_STATE_ACTIVE) {
public changeState(state: AppState): void {
if (state === AppState.APP_STATE_ACTIVE) {
this.mgmtService.reactivateApp(this.projectId, this.app.id).then(() => {
this.app.state = state;
this.toast.showInfo('APP.TOAST.REACTIVATED', true);
}).catch((error: any) => {
this.toast.showError(error);
});
} else if (event.value === AppState.APP_STATE_INACTIVE) {
} else if (state === AppState.APP_STATE_INACTIVE) {
this.mgmtService.deactivateApp(this.projectId, this.app.id).then(() => {
this.app.state = state;
this.toast.showInfo('APP.TOAST.DEACTIVATED', true);
}).catch((error: any) => {
this.toast.showError(error);

View File

@ -29,12 +29,12 @@
<ng-container matColumnDef="externalUserDisplayName">
<th mat-header-cell *matHeaderCellDef> {{ 'USER.EXTERNALIDP.USERDISPLAYNAME' | translate }} </th>
<td mat-cell *matCellDef="let idp"> {{idp?.externalUserDisplayName}} </td>
<td mat-cell *matCellDef="let idp"> {{idp?.providedUserName}} </td>
</ng-container>
<ng-container matColumnDef="externalUserId">
<th mat-header-cell *matHeaderCellDef> {{ 'USER.EXTERNALIDP.EXTERNALUSERID' | translate }} </th>
<td mat-cell *matCellDef="let idp"> {{idp?.externalUserId}} </td>
<td mat-cell *matCellDef="let idp"> {{idp?.providedUserId}} </td>
</ng-container>
<ng-container matColumnDef="actions" stickyEnd>

View File

@ -27,10 +27,13 @@
<ng-container matColumnDef="memberType">
<th mat-header-cell *matHeaderCellDef> {{ 'USER.MEMBERSHIPS.TYPE' | translate }} </th>
<td class="pointer" mat-cell *matCellDef="let member">
<span *ngIf="member.orgId && !member.projectGrantId && !member.projectId"> {{'USER.MEMBERSHIPS.TYPES.ORG' | translate }}</span>
<span *ngIf="member.projectId && !member.projectGrantId"> {{'USER.MEMBERSHIPS.TYPES.PROJECT' | translate }}</span>
<span *ngIf="member.projectId && member.projectGrantId"> {{'USER.MEMBERSHIPS.TYPES.GRANTEDPROJECT' | translate }}</span>
</td>
<span *ngIf="member.orgId && !member.projectGrantId && !member.projectId">
{{'USER.MEMBERSHIPS.TYPES.ORG' | translate }}</span>
<span *ngIf="member.projectId && !member.projectGrantId"> {{'USER.MEMBERSHIPS.TYPES.PROJECT' |
translate }}</span>
<span *ngIf="member.projectId && member.projectGrantId">
{{'USER.MEMBERSHIPS.TYPES.GRANTEDPROJECT' | translate }}</span>
</td>
</ng-container>
<ng-container matColumnDef="displayName">
@ -59,6 +62,17 @@
</td>
</ng-container>
<ng-container matColumnDef="actions" stickyEnd>
<th mat-header-cell *matHeaderCellDef></th>
<td mat-cell *matCellDef="let member">
<button [disabled]="member.projectId && member.projectGrantId ? (['project.grant.member.delete:' + member.projectGrantId, 'project.grant.member.delete'] | hasRole | async) == false : member.projectId ? (['project.member.delete:' + member.projectGrantId, 'project.member.delete'] | hasRole | async) == false : member.orgId ? (['org.member.delete:' + member.orgId, 'org.member.delete'] | hasRole | async) == false : (['iam.member.delete'] | hasRole | async) == false"
mat-icon-button color="warn" matTooltip="{{'ACTIONS.REMOVE' | translate}}"
(click)="removeMembership(member)">
<i class="las la-minus-circle"></i>
</button>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr class="highlight" mat-row *matRowDef="let row; columns: displayedColumns;">
</tr>

View File

@ -34,3 +34,19 @@
outline: none;
cursor: pointer;
}
tr {
outline: none;
button {
visibility: hidden;
}
&:hover {
button {
visibility: visible;
}
}
}

View File

@ -30,7 +30,7 @@ export class MembershipDetailComponent implements AfterViewInit {
public memberRoleOptions: string[] = [];
/** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */
public displayedColumns: string[] = ['select', 'memberType', 'displayName', 'creationDate', 'changeDate', 'roles'];
public displayedColumns: string[] = ['select', 'memberType', 'displayName', 'creationDate', 'changeDate', 'roles', 'actions'];
public loading: boolean = false;
public memberships!: Membership.AsObject[];
@ -203,6 +203,27 @@ export class MembershipDetailComponent implements AfterViewInit {
}
}
public removeMembership(membership: Membership.AsObject): void {
let prom;
if (membership.projectId && membership.projectGrantId && membership.userId) {
prom = this.mgmtService.removeProjectGrantMember(membership.projectId, membership.projectGrantId, membership.userId);
} else if (membership.projectId && membership.userId) {
prom = this.mgmtService.removeProjectMember(membership.projectId, membership.userId);
} else if (membership.orgId && membership.userId) {
prom = this.mgmtService.removeOrgMember(membership.userId);
} else if (membership.userId) {
prom = this.adminService.removeIAMMember(membership.userId);
}
if (prom) {
prom.then(() => {
this.toast.showInfo('PROJECT.TOAST.MEMBERREMOVED', true);
this.refreshPage();
}).catch(error => this.toast.showError(error));
}
}
public refreshPage(): void {
this.selection.clear();
this.dataSource.loadMemberships(this.user.id, this.paginator.pageIndex, this.paginator.pageSize);

View File

@ -12,6 +12,7 @@ import { TranslateModule } from '@ngx-translate/core';
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
import { DetailLayoutModule } from 'src/app/modules/detail-layout/detail-layout.module';
import { RefreshTableModule } from 'src/app/modules/refresh-table/refresh-table.module';
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module';
import { LocalizedDatePipeModule } from 'src/app/pipes/localized-date-pipe/localized-date-pipe.module';
import { TimestampToDatePipeModule } from 'src/app/pipes/timestamp-to-date-pipe/timestamp-to-date-pipe.module';
@ -43,6 +44,7 @@ const routes: Routes = [
HasRoleModule,
MatIconModule,
MatButtonModule,
HasRolePipeModule,
RefreshTableModule,
MatTooltipModule,
],

View File

@ -10,8 +10,8 @@
[ngStyle]="{'z-index': 100 - i}">
<div class="membership-avatar" [ngStyle]="{'background-color': getColor(membership)}">
<i *ngIf="membership.orgId" class="las la-archway"></i>
<i *ngIf="membership.projectId && !membership.grantId" class="icon las la-layer-group"></i>
<i *ngIf="membership.projectId && membership.grantId" class="icon las la-layer-group"></i>
<i *ngIf="membership.projectId && !membership.projectGrantId" class="icon las la-layer-group"></i>
<i *ngIf="membership.projectId && membership.projectGrantId" class="icon las la-layer-group"></i>
<span>{{membership.displayName}}</span>
</div>
@ -25,6 +25,7 @@
</div>
</div>
</ng-template>
<button [disabled]="disabled" class="add-img" (click)="addMember()" mat-icon-button
matTooltip="{{'ACTIONS.ADD' | translate}}" aria-label="add membership">
<mat-icon>add</mat-icon>

View File

@ -15,11 +15,11 @@
<ng-template appHasRole [appHasRole]="['user.write$', 'user.write:'+user?.id]">
<button class="state-button" mat-stroked-button color="warn"
*ngIf="user?.state === UserState.USER_STATE_ACTIVE"
*ngIf="user?.state !== UserState.USER_STATE_INACTIVE"
(click)="changeState(UserState.USER_STATE_INACTIVE)">{{'USER.PAGES.DEACTIVATE' |
translate}}</button>
<button class="state-button" mat-stroked-button color="warn"
*ngIf="user?.state === UserState.USER_STATE_INACTIVE"
*ngIf="user?.state !== UserState.USER_STATE_ACTIVE"
(click)="changeState(UserState.USER_STATE_ACTIVE)">{{'USER.PAGES.REACTIVATE' | translate}}</button>
</ng-template>
</div>

View File

@ -48,10 +48,10 @@
<ng-container matColumnDef="displayName">
<th mat-header-cell *matHeaderCellDef
[ngClass]="{'search-active': this.userSearchKey == UserListSearchKey.USERSEARCHKEY_DISPLAY_NAME}">
[ngClass]="{'search-active': this.userSearchKey == UserListSearchKey.DISPLAY_NAME}">
{{ 'USER.PROFILE.DISPLAYNAME' | translate }}
<template [ngTemplateOutlet]="templateRef"
[ngTemplateOutletContext]="{key: UserListSearchKey.USERSEARCHKEY_DISPLAY_NAME}"></template>
[ngTemplateOutletContext]="{key: UserListSearchKey.DISPLAY_NAME}"></template>
</th>
<td mat-cell *matCellDef="let user" [routerLink]="user.id ? ['/users', user.id ]: null">
{{user.human?.profile?.displayName}}

View File

@ -0,0 +1,18 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { TimestampToRetentionPipe } from './timestamp-to-retention.pipe';
@NgModule({
declarations: [
TimestampToRetentionPipe,
],
imports: [
CommonModule,
],
exports: [
TimestampToRetentionPipe,
],
})
export class TimestampToRetentionPipeModule { }

View File

@ -0,0 +1,25 @@
import { Pipe, PipeTransform } from '@angular/core';
import { Duration } from 'google-protobuf/google/protobuf/duration_pb';
@Pipe({
name: 'timestampToRetention',
})
export class TimestampToRetentionPipe implements PipeTransform {
transform(value?: Duration.AsObject, ...args: unknown[]): unknown {
if (value) {
return this.retentionFromTimestamp(value);
} else {
return '';
}
}
private retentionFromTimestamp(date: Duration.AsObject): any {
if (date?.seconds !== undefined && date?.nanos !== undefined) {
const ms = (date.seconds * 1000 + date.nanos / 1000 / 1000);
const mins = ms / 1000 / 60;
return mins / 60;
}
}
}

View File

@ -173,6 +173,7 @@
"DEACTIVATE":"Deaktivieren",
"ACTIVATE":"Aktivieren",
"FILTER": {
"0":"Nach displayName filtern",
"1":"Nach Username filtern",
"2":"Nach Vornamen filtern",
"3":"Nach Nachnamen filtern",
@ -588,7 +589,8 @@
"2":"Annuliert",
"3":"Besitzstandswahrend"
},
"NOTAVAILABLE":"Feature {{value}} ist auf Ihrer organisation nicht freigeschaltet!"
"NOTAVAILABLE":"Feature {{value}} ist auf Ihrer organisation nicht freigeschaltet!",
"RETENTIONHOURS":"Stunden"
},
"POLICY": {
"TITLE":"Richtlinen entdecken",

View File

@ -173,6 +173,7 @@
"DEACTIVATE":"Deactivate",
"ACTIVATE":"Activate",
"FILTER": {
"0":"Filter for DisplayName",
"1":"Filter for Username",
"2":"filter for Firstname",
"3":"filter for Lastname",
@ -586,7 +587,8 @@
"2":"Cancelled",
"3":"Grandfathered"
},
"NOTAVAILABLE":"Feature {{value}} is missing on your organization."
"NOTAVAILABLE":"Feature {{value}} is missing on your organization.",
"RETENTIONHOURS":"Hours"
},
"POLICY": {
"TITLE":"Explore Policies",