feat(console): profile initializers, contributors for grants (#229)

* resourceowner header, i18n

* password policy validators, roles required field

* use angular pattern instead of custom validator

* user detail fixes, mfa qr code, add org

* use mgmt for mfa list

* fetch owned projects

* search project

* seperate owned from granted projects

* lint

* fix granted project grid

* refactor project detail

* disable zitadel apps

* refactor project contributors

* changed i18n

* hide meta nav

* mobile meta layout

* refactor contributor name

* refactor org contributors

* org i18n, org member detail view

* fix uninitialized phone, email

* fix: create role, add contributors to granted pjs

* initialize address

* contributor 18n

* add tab module
This commit is contained in:
Max Peintner 2020-06-17 07:41:16 +02:00 committed by GitHub
parent 6fa62ccd0a
commit dfe6d0deb4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 80 additions and 59 deletions

View File

@ -9,6 +9,7 @@ import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input'; import { MatInputModule } from '@angular/material/input';
import { MatMenuModule } from '@angular/material/menu'; import { MatMenuModule } from '@angular/material/menu';
import { MatTabsModule } from '@angular/material/tabs';
import { MatTooltipModule } from '@angular/material/tooltip'; import { MatTooltipModule } from '@angular/material/tooltip';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { HttpLoaderFactory } from 'src/app/app.module'; import { HttpLoaderFactory } from 'src/app/app.module';
@ -40,6 +41,7 @@ import { PolicyGridComponent } from './policy-grid/policy-grid.component';
ReactiveFormsModule, ReactiveFormsModule,
MatButtonToggleModule, MatButtonToggleModule,
MetaLayoutModule, MetaLayoutModule,
MatTabsModule,
MatTooltipModule, MatTooltipModule,
MatMenuModule, MatMenuModule,
ChangesModule, ChangesModule,

View File

@ -18,8 +18,8 @@
<mat-form-field appearance="outline" class="formfield"> <mat-form-field appearance="outline" class="formfield">
<mat-label>{{ 'PROJECT.ROLE.KEY' | translate }}</mat-label> <mat-label>{{ 'PROJECT.ROLE.KEY' | translate }}</mat-label>
<input matInput formControlName="key" /> <input matInput formControlName="key" />
<mat-error *ngIf="formGroup.get('key')?.errors?.required">{{'ERRORS.REQUIRED' | translate}} <!-- <mat-error *ngIf="formGroup.get('key')?.errors?.required">{{'ERRORS.REQUIRED' | translate}}
</mat-error> </mat-error> -->
</mat-form-field> </mat-form-field>
<mat-form-field appearance="outline" class="formfield"> <mat-form-field appearance="outline" class="formfield">
<mat-label>{{ 'PROJECT.ROLE.DISPLAY_NAME' | translate }}</mat-label> <mat-label>{{ 'PROJECT.ROLE.DISPLAY_NAME' | translate }}</mat-label>

View File

@ -1,7 +1,7 @@
import { animate, animateChild, query, stagger, style, transition, trigger } from '@angular/animations'; import { animate, animateChild, query, stagger, style, transition, trigger } from '@angular/animations';
import { Location } from '@angular/common'; import { Location } from '@angular/common';
import { Component, OnDestroy, OnInit } from '@angular/core'; import { Component, OnDestroy, OnInit } from '@angular/core';
import { FormArray, FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; import { FormArray, FormControl, FormGroup, Validators } from '@angular/forms';
import { ActivatedRoute, Params, Router } from '@angular/router'; import { ActivatedRoute, Params, Router } from '@angular/router';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { ProjectRoleAdd } from 'src/app/proto/generated/management_pb'; import { ProjectRoleAdd } from 'src/app/proto/generated/management_pb';
@ -47,11 +47,9 @@ export class ProjectRoleCreateComponent implements OnInit, OnDestroy {
private toast: ToastService, private toast: ToastService,
private projectService: ProjectService, private projectService: ProjectService,
private _location: Location, private _location: Location,
private fb: FormBuilder,
) { ) {
this.formGroup = new FormGroup({ this.formGroup = new FormGroup({
key: new FormControl('', [Validators.required]), key: new FormControl('', [Validators.required]),
// name: new FormControl(''),
displayName: new FormControl(''), displayName: new FormControl(''),
group: new FormControl('', [Validators.required]), group: new FormControl('', [Validators.required]),
}); });
@ -61,7 +59,7 @@ export class ProjectRoleCreateComponent implements OnInit, OnDestroy {
public addEntry(): void { public addEntry(): void {
const newGroup = new FormGroup({ const newGroup = new FormGroup({
name: new FormControl(''), key: new FormControl(''),
displayName: new FormControl(''), displayName: new FormControl(''),
group: new FormControl('', [Validators.required]), group: new FormControl('', [Validators.required]),
}); });
@ -86,11 +84,13 @@ export class ProjectRoleCreateComponent implements OnInit, OnDestroy {
} }
public addRole(): void { public addRole(): void {
Promise.all(this.formArray.value.map((role: ProjectRoleAdd.AsObject) => { const promises = this.formArray.value.map((role: ProjectRoleAdd.AsObject) => {
role.id = this.projectId; role.id = this.projectId;
console.log(role); console.log(role);
return this.projectService.AddProjectRole(role); return this.projectService.AddProjectRole(role);
})).then(() => { });
Promise.all(promises).then(() => {
this.router.navigate(['projects', this.projectId]); this.router.navigate(['projects', this.projectId]);
}).catch(data => { }).catch(data => {
console.log(data); console.log(data);

View File

@ -31,6 +31,16 @@
</div> </div>
</div> </div>
<app-changes *ngIf="project" [changeType]="ChangeType.PROJECT" [id]="project.id"></app-changes> <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"
[projectType]="ProjectType.PROJECTTYPE_GRANTED" [project]="project">
</app-project-contributors>
</mat-tab>
<mat-tab label="{{ 'CHANGES.PROJECT.TITLE' | translate }}" class="flex-col">
<app-changes *ngIf="project" [changeType]="ChangeType.PROJECT" [id]="project.id"></app-changes>
</mat-tab>
</mat-tab-group>
</metainfo> </metainfo>
</app-meta-layout> </app-meta-layout>

View File

@ -88,10 +88,10 @@
<mat-tab-group mat-stretch-tabs class="tab-group" disablePagination="true"> <mat-tab-group mat-stretch-tabs class="tab-group" disablePagination="true">
<mat-tab label="Details"> <mat-tab label="Details">
<app-owned-project-contributors *ngIf="project" <app-project-contributors *ngIf="project"
[disabled]="project?.state !== ProjectState.PROJECTSTATE_ACTIVE" [disabled]="project?.state !== ProjectState.PROJECTSTATE_ACTIVE"
[projectType]="ProjectType.PROJECTTYPE_OWNED" [project]="project"> [projectType]="ProjectType.PROJECTTYPE_OWNED" [project]="project">
</app-owned-project-contributors> </app-project-contributors>
</mat-tab> </mat-tab>
<mat-tab label="{{ 'CHANGES.PROJECT.TITLE' | translate }}" class="flex-col"> <mat-tab label="{{ 'CHANGES.PROJECT.TITLE' | translate }}" class="flex-col">
<app-changes *ngIf="project" [changeType]="ChangeType.PROJECT" [id]="project.id"></app-changes> <app-changes *ngIf="project" [changeType]="ChangeType.PROJECT" [id]="project.id"></app-changes>

View File

@ -1,20 +1,20 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { OwnedProjectContributorsComponent } from './owned-project-contributors.component'; import { ProjectContributorsComponent } from './project-contributors.component';
describe('ProjectContributorsComponent', () => { describe('ProjectContributorsComponent', () => {
let component: OwnedProjectContributorsComponent; let component: ProjectContributorsComponent;
let fixture: ComponentFixture<OwnedProjectContributorsComponent>; let fixture: ComponentFixture<ProjectContributorsComponent>;
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
declarations: [OwnedProjectContributorsComponent], declarations: [ProjectContributorsComponent],
}) })
.compileComponents(); .compileComponents();
})); }));
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(OwnedProjectContributorsComponent); fixture = TestBed.createComponent(ProjectContributorsComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
fixture.detectChanges(); fixture.detectChanges();
}); });

View File

@ -5,6 +5,7 @@ import { BehaviorSubject, from, of } from 'rxjs';
import { catchError, finalize, map } from 'rxjs/operators'; import { catchError, finalize, map } from 'rxjs/operators';
import { User } from 'src/app/proto/generated/auth_pb'; import { User } from 'src/app/proto/generated/auth_pb';
import { import {
ProjectGrantView,
ProjectMemberSearchResponse, ProjectMemberSearchResponse,
ProjectMemberView, ProjectMemberView,
ProjectState, ProjectState,
@ -20,12 +21,12 @@ import {
} from '../../../modules/add-member-dialog/member-create-dialog.component'; } from '../../../modules/add-member-dialog/member-create-dialog.component';
@Component({ @Component({
selector: 'app-owned-project-contributors', selector: 'app-project-contributors',
templateUrl: './owned-project-contributors.component.html', templateUrl: './project-contributors.component.html',
styleUrls: ['./owned-project-contributors.component.scss'], styleUrls: ['./project-contributors.component.scss'],
}) })
export class OwnedProjectContributorsComponent implements OnInit { export class ProjectContributorsComponent implements OnInit {
@Input() public project!: ProjectView.AsObject; @Input() public project!: ProjectView.AsObject | ProjectGrantView.AsObject;
@Input() public projectType!: ProjectType; @Input() public projectType!: ProjectType;
@Input() public disabled: boolean = false; @Input() public disabled: boolean = false;

View File

@ -33,12 +33,12 @@ import { UserListModule } from '../user-list/user-list.module';
import { GrantedProjectDetailComponent } from './granted-project-detail/granted-project-detail.component'; import { GrantedProjectDetailComponent } from './granted-project-detail/granted-project-detail.component';
import { GrantedProjectGridComponent } from './granted-project-grid/granted-project-grid.component'; import { GrantedProjectGridComponent } from './granted-project-grid/granted-project-grid.component';
import { GrantedProjectListComponent } from './granted-project-list/granted-project-list.component'; import { GrantedProjectListComponent } from './granted-project-list/granted-project-list.component';
import { OwnedProjectContributorsComponent } from './owned-project-contributors/owned-project-contributors.component';
import { OwnedProjectDetailComponent } from './owned-project-detail/owned-project-detail.component'; import { OwnedProjectDetailComponent } from './owned-project-detail/owned-project-detail.component';
import { OwnedProjectGridComponent } from './owned-project-grid/owned-project-grid.component'; import { OwnedProjectGridComponent } from './owned-project-grid/owned-project-grid.component';
import { OwnedProjectListComponent } from './owned-project-list/owned-project-list.component'; import { OwnedProjectListComponent } from './owned-project-list/owned-project-list.component';
import { ProjectApplicationGridComponent } from './project-application-grid/project-application-grid.component'; import { ProjectApplicationGridComponent } from './project-application-grid/project-application-grid.component';
import { ProjectApplicationsComponent } from './project-applications/project-applications.component'; import { ProjectApplicationsComponent } from './project-applications/project-applications.component';
import { ProjectContributorsComponent } from './project-contributors/project-contributors.component';
import { import {
ProjectGrantMembersCreateDialogComponent, ProjectGrantMembersCreateDialogComponent,
} from './project-grant-members-create-dialog/project-grant-members-create-dialog.component'; } from './project-grant-members-create-dialog/project-grant-members-create-dialog.component';
@ -62,7 +62,7 @@ import { ProjectsComponent } from './projects.component';
ProjectGrantsComponent, ProjectGrantsComponent,
ProjectGrantMembersComponent, ProjectGrantMembersComponent,
ProjectGrantMembersCreateDialogComponent, ProjectGrantMembersCreateDialogComponent,
OwnedProjectContributorsComponent, ProjectContributorsComponent,
ProjectsComponent, ProjectsComponent,
], ],
imports: [ imports: [

View File

@ -38,11 +38,6 @@ export class UserCreateComponent implements OnDestroy {
region: [''], region: [''],
country: [''], country: [''],
}); });
if (this.email) {
this.sub = this.email?.valueChanges.subscribe(value => {
this.userName?.setValue(value);
});
}
} }
public createUser(): void { public createUser(): void {

View File

@ -68,16 +68,16 @@
<app-card *ngIf="profile" title="{{ 'USER.LOGINMETHODS.TITLE' | translate }}" <app-card *ngIf="profile" title="{{ 'USER.LOGINMETHODS.TITLE' | translate }}"
description="{{ 'USER.LOGINMETHODS.DESCRIPTION' | translate }}"> description="{{ 'USER.LOGINMETHODS.DESCRIPTION' | translate }}">
<div class="method-col"> <div class="method-col">
<div *ngIf="email" class="method-row"> <div class="method-row">
<span class="label">{{ 'USER.EMAIL' | translate }}</span> <span class="label">{{ 'USER.EMAIL' | translate }}</span>
<ng-container *ngIf="!emailEditState; else emailEdit"> <ng-container *ngIf="!emailEditState; else emailEdit">
<div class="actions"> <div class="actions">
<span class="name">{{email.email}}</span> <span class="name">{{email?.email}}</span>
<mat-icon *ngIf="email.isemailverified" color="primary" aria-hidden="false" <mat-icon *ngIf="email?.isemailverified" color="primary" aria-hidden="false"
aria-label="verified icon"> aria-label="verified icon">
check_circle_outline</mat-icon> check_circle_outline</mat-icon>
<ng-container *ngIf="email.email && !email.isemailverified"> <ng-container *ngIf="email?.email && !email?.isemailverified">
<mat-icon color="warn" aria-hidden="false" aria-label="not verified icon">highlight_off <mat-icon color="warn" aria-hidden="false" aria-label="not verified icon">highlight_off
</mat-icon> </mat-icon>
<a class="verify" matTooltip="{{'USER.LOGINMETHODS.EMAIL.RESEND' | translate}}" <a class="verify" matTooltip="{{'USER.LOGINMETHODS.EMAIL.RESEND' | translate}}"
@ -104,16 +104,16 @@
</ng-template> </ng-template>
</div> </div>
<div *ngIf="phone" class="method-row"> <div class="method-row">
<span class="label">{{ 'USER.PHONE' | translate }}</span> <span class="label">{{ 'USER.PHONE' | translate }}</span>
<ng-container *ngIf="!phoneEditState; else phoneEdit"> <ng-container *ngIf="!phoneEditState; else phoneEdit">
<div class="actions"> <div class="actions">
<span class="name">{{phone.phone}}</span> <span class="name">{{phone?.phone}}</span>
<mat-icon *ngIf="phone.isPhoneVerified" color="primary" aria-hidden="false" <mat-icon *ngIf="phone?.isPhoneVerified" color="primary" aria-hidden="false"
aria-label="verified icon"> aria-label="verified icon">
check_circle_outline</mat-icon> check_circle_outline</mat-icon>
<ng-container *ngIf="phone.phone && !phone.isPhoneVerified"> <ng-container *ngIf="phone?.phone && !phone?.isPhoneVerified">
<mat-icon color="warn" aria-hidden="false" aria-label="not verified icon">highlight_off <mat-icon color="warn" aria-hidden="false" aria-label="not verified icon">highlight_off
</mat-icon> </mat-icon>
<a class="verify" matTooltip="{{'USER.LOGINMETHODS.ENTERCODE_DESC' | translate}}" <a class="verify" matTooltip="{{'USER.LOGINMETHODS.ENTERCODE_DESC' | translate}}"
@ -127,7 +127,7 @@
<button (click)="phoneEditState = true" mat-icon-button> <button (click)="phoneEditState = true" mat-icon-button>
<mat-icon>edit</mat-icon> <mat-icon>edit</mat-icon>
</button> </button>
<button *ngIf="phone.phone" (click)="deletePhone()" mat-icon-button> <button *ngIf="phone?.phone" (click)="deletePhone()" mat-icon-button>
<mat-icon>delete_outline</mat-icon> <mat-icon>delete_outline</mat-icon>
</button> </button>
</div> </div>
@ -150,7 +150,7 @@
<app-auth-user-mfa *ngIf="profile" [profile]="profile"></app-auth-user-mfa> <app-auth-user-mfa *ngIf="profile" [profile]="profile"></app-auth-user-mfa>
<app-card title="{{ 'USER.ADDRESS.TITLE' | translate }}" *ngIf="address && profile"> <app-card title="{{ 'USER.ADDRESS.TITLE' | translate }}" *ngIf="profile">
<form [formGroup]="addressForm" (ngSubmit)="saveAddress()"> <form [formGroup]="addressForm" (ngSubmit)="saveAddress()">
<div class="content"> <div class="content">
<mat-form-field class="formfield"> <mat-form-field class="formfield">

View File

@ -33,9 +33,9 @@ function passwordConfirmValidator(c: AbstractControl): any {
}) })
export class AuthUserDetailComponent implements OnDestroy { export class AuthUserDetailComponent implements OnDestroy {
public profile!: UserProfile.AsObject; public profile!: UserProfile.AsObject;
public email!: UserEmail.AsObject; public email: UserEmail.AsObject = { email: '' } as any;
public phone!: UserPhone.AsObject; public phone: UserPhone.AsObject = { phone: '' } as any;
public address!: UserAddress.AsObject; public address: UserAddress.AsObject = { id: '' } as any;
public genders: Gender[] = [Gender.GENDER_MALE, Gender.GENDER_FEMALE, Gender.GENDER_DIVERSE]; public genders: Gender[] = [Gender.GENDER_MALE, Gender.GENDER_FEMALE, Gender.GENDER_DIVERSE];
public languages: string[] = ['de', 'en']; public languages: string[] = ['de', 'en'];
@ -150,6 +150,8 @@ export class AuthUserDetailComponent implements OnDestroy {
} }
public saveEmail(): void { public saveEmail(): void {
this.emailEditState = false;
this.userService this.userService
.SaveMyUserEmail(this.email).then((data: UserEmail) => { .SaveMyUserEmail(this.email).then((data: UserEmail) => {
this.toast.showInfo('Saved Email'); this.toast.showInfo('Saved Email');
@ -211,6 +213,10 @@ export class AuthUserDetailComponent implements OnDestroy {
} }
public savePhone(): void { public savePhone(): void {
this.phoneEditState = false;
if (!this.phone.id) {
this.phone.id = this.profile.id;
}
this.userService this.userService
.SaveMyUserPhone(this.phone).then((data: UserPhone) => { .SaveMyUserPhone(this.phone).then((data: UserPhone) => {
this.toast.showInfo('Saved Phone'); this.toast.showInfo('Saved Phone');

View File

@ -67,16 +67,16 @@
<app-card title="{{ 'USER.LOGINMETHODS.TITLE' | translate }}" <app-card title="{{ 'USER.LOGINMETHODS.TITLE' | translate }}"
description="{{ 'USER.LOGINMETHODS.DESCRIPTION' | translate }}"> description="{{ 'USER.LOGINMETHODS.DESCRIPTION' | translate }}">
<div class="method-col"> <div class="method-col">
<div *ngIf="email" class="method-row"> <div class="method-row">
<span class="label">{{ 'USER.EMAIL' | translate }}</span> <span class="label">{{ 'USER.EMAIL' | translate }}</span>
<ng-container *ngIf="!emailEditState; else emailEdit"> <ng-container *ngIf="!emailEditState; else emailEdit">
<div class="actions"> <div class="actions">
<span class="name">{{email.email}}</span> <span class="name">{{email?.email}}</span>
<mat-icon *ngIf="email.isEmailVerified" color="primary" aria-hidden="false" <mat-icon *ngIf="email?.isEmailVerified" color="primary" aria-hidden="false"
aria-label="verified icon"> aria-label="verified icon">
check_circle_outline</mat-icon> check_circle_outline</mat-icon>
<ng-container *ngIf="email.email && !email.isEmailVerified"> <ng-container *ngIf="email?.email && !email?.isEmailVerified">
<mat-icon color="warn" aria-hidden="false" aria-label="not verified icon">highlight_off <mat-icon color="warn" aria-hidden="false" aria-label="not verified icon">highlight_off
</mat-icon> </mat-icon>
<a class="verify" matTooltip="{{'USER.LOGINMETHODS.EMAIL.RESEND' | translate}}" <a class="verify" matTooltip="{{'USER.LOGINMETHODS.EMAIL.RESEND' | translate}}"
@ -103,16 +103,16 @@
</ng-template> </ng-template>
</div> </div>
<div *ngIf="phone" class="method-row"> <div class="method-row">
<span class="label">{{ 'USER.PHONE' | translate }}</span> <span class="label">{{ 'USER.PHONE' | translate }}</span>
<ng-container *ngIf="!phoneEditState; else phoneEdit"> <ng-container *ngIf="!phoneEditState; else phoneEdit">
<div class="actions"> <div class="actions">
<span class="name">{{phone.phone}}</span> <span class="name">{{phone?.phone}}</span>
<mat-icon *ngIf="phone.isPhoneVerified" color="primary" aria-hidden="false" <mat-icon *ngIf="phone?.isPhoneVerified" color="primary" aria-hidden="false"
aria-label="verified icon"> aria-label="verified icon">
check_circle_outline</mat-icon> check_circle_outline</mat-icon>
<ng-container *ngIf="phone.phone && !phone.isPhoneVerified"> <ng-container *ngIf="phone?.phone && !phone?.isPhoneVerified">
<mat-icon color="warn" aria-hidden="false" aria-label="not verified icon">highlight_off <mat-icon color="warn" aria-hidden="false" aria-label="not verified icon">highlight_off
</mat-icon> </mat-icon>
@ -125,7 +125,7 @@
<button (click)="phoneEditState = true" mat-icon-button> <button (click)="phoneEditState = true" mat-icon-button>
<mat-icon>edit</mat-icon> <mat-icon>edit</mat-icon>
</button> </button>
<button *ngIf="phone.phone" (click)="deletePhone()" mat-icon-button> <button *ngIf="phone?.phone" (click)="deletePhone()" mat-icon-button>
<mat-icon>delete_outline</mat-icon> <mat-icon>delete_outline</mat-icon>
</button> </button>
</div> </div>
@ -148,7 +148,7 @@
<app-auth-user-mfa *ngIf="profile" [profile]="profile"></app-auth-user-mfa> <app-auth-user-mfa *ngIf="profile" [profile]="profile"></app-auth-user-mfa>
<app-card title="{{ 'USER.ADDRESS.TITLE' | translate }}" *ngIf="address"> <app-card title="{{ 'USER.ADDRESS.TITLE' | translate }}">
<form [formGroup]="addressForm" (ngSubmit)="saveAddress()"> <form [formGroup]="addressForm" (ngSubmit)="saveAddress()">
<div class="content"> <div class="content">
<mat-form-field class="formfield"> <mat-form-field class="formfield">

View File

@ -44,9 +44,9 @@ function passwordConfirmValidator(c: AbstractControl): any {
}) })
export class UserDetailComponent implements OnInit, OnDestroy { export class UserDetailComponent implements OnInit, OnDestroy {
public profile!: UserProfile.AsObject; public profile!: UserProfile.AsObject;
public email!: UserEmail.AsObject; public email: UserEmail.AsObject = { email: '' } as any;
public phone!: UserPhone.AsObject; public phone: UserPhone.AsObject = { phone: '' } as any;
public address!: UserAddress.AsObject; public address: UserAddress.AsObject = { id: '' } as any;
public genders: Gender[] = [Gender.GENDER_MALE, Gender.GENDER_FEMALE, Gender.GENDER_DIVERSE]; public genders: Gender[] = [Gender.GENDER_MALE, Gender.GENDER_FEMALE, Gender.GENDER_DIVERSE];
public languages: string[] = ['de', 'en']; public languages: string[] = ['de', 'en'];
@ -222,6 +222,9 @@ export class UserDetailComponent implements OnInit, OnDestroy {
public savePhone(): void { public savePhone(): void {
this.phoneEditState = false; this.phoneEditState = false;
if (!this.phone.id) {
this.phone.id = this.profile.id;
}
this.mgmtUserService this.mgmtUserService
.SaveUserPhone(this.phone).then((data: UserPhone) => { .SaveUserPhone(this.phone).then((data: UserPhone) => {
this.toast.showInfo('Saved Phone'); this.toast.showInfo('Saved Phone');
@ -232,6 +235,10 @@ export class UserDetailComponent implements OnInit, OnDestroy {
} }
public saveAddress(): void { public saveAddress(): void {
if (!this.address.id) {
this.address.id = this.profile.id;
}
this.address.streetAddress = this.streetAddress?.value; this.address.streetAddress = this.streetAddress?.value;
this.address.postalCode = this.postalCode?.value; this.address.postalCode = this.postalCode?.value;
this.address.locality = this.locality?.value; this.address.locality = this.locality?.value;

View File

@ -296,8 +296,8 @@
}, },
"NAME": "Name", "NAME": "Name",
"MEMBER": { "MEMBER": {
"TITLE": "Mitwirkende", "TITLE": "Manager",
"TITLEDESC":"Mitwirkende können Änderungen an dieser Entität vornehmen", "TITLEDESC":"Manager können Änderungen an dieser Entität vornehmen",
"DESCRIPTION":"Hier finden Sie alle Mitwirkenden dieses Projekts. Sie können ein neues Mitglied hinzufügen und bestehende verwalten.", "DESCRIPTION":"Hier finden Sie alle Mitwirkenden dieses Projekts. Sie können ein neues Mitglied hinzufügen und bestehende verwalten.",
"USERNAME": "Benutzername", "USERNAME": "Benutzername",
"FIRSTNAME": "Vorname", "FIRSTNAME": "Vorname",

View File

@ -296,9 +296,9 @@
}, },
"NAME": "Name", "NAME": "Name",
"MEMBER": { "MEMBER": {
"TITLE": "Contributors", "TITLE": "Managers",
"TITLEDESC":"Contributors can make changes on this very entity", "TITLEDESC":"Managers can make changes on this very entity, based on their given role in zitadel.",
"DESCRIPTION":"Here you can find all contributors of this project. You can add a new member and manage persisting ones.", "DESCRIPTION":"Here you can find all managers of this project. You can add a new member and manage persisting ones.",
"USERNAME": "Username", "USERNAME": "Username",
"FIRSTNAME": "Firstname", "FIRSTNAME": "Firstname",
"LASTNAME": "Lastname", "LASTNAME": "Lastname",