mirror of
https://github.com/andwati/zitadel.git
synced 2026-11-06 22:32:16 +00:00
feat(console): pinned org list, project grant detail view, state change, i18n, domain dialog, policy refactor, theme fixes, refactor user and projects (#449)
* pinned organisations * project grant detail, state update, user-list pad * rm entry components * members nav, i18n, disable actions on non active * add org domain dialog * mv password policy rm to detail view * prefix pinned orgs for userid, fix collapsed pad * fix app back navigation * rem pwd required validator * fix org item overflow * routing * move users modules to users page * reorganize projects * remove child init of translate * hide same preferred loginname
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
|
||||
import { UserCreateComponent } from './user-create.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: UserCreateComponent,
|
||||
data: { animation: 'DetailPage' },
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class UserCreateRoutingModule { }
|
||||
@@ -0,0 +1,136 @@
|
||||
<div class="max-width-container">
|
||||
<div class="container">
|
||||
<div class="left">
|
||||
<a [routerLink]="[ '/users']" mat-icon-button>
|
||||
<mat-icon class="icon">arrow_back</mat-icon>
|
||||
</a>
|
||||
</div>
|
||||
<div class="right">
|
||||
<div class="head">
|
||||
|
||||
<h1>{{ 'USER.CREATE.TITLE' | translate }}</h1>
|
||||
|
||||
<p class="desc">{{ 'USER.CREATE.DESCRIPTION' | translate }}</p>
|
||||
</div>
|
||||
|
||||
<form *ngIf="userForm" [formGroup]="userForm" (ngSubmit)="createUser()" class="form">
|
||||
<!-- <h2>{{ 'USER.PAGES.CREATE' | translate}}</h2> -->
|
||||
<div class="content">
|
||||
<p class="section">{{ 'USER.CREATE.NAMEANDEMAILSECTION' | translate }}</p>
|
||||
<mat-form-field class="formfield">
|
||||
<mat-label>{{ 'USER.PROFILE.EMAIL' | translate }}</mat-label>
|
||||
<input matInput formControlName="email" required />
|
||||
<mat-error *ngIf="email?.invalid && email?.errors?.required">
|
||||
{{ 'USER.VALIDATION.REQUIRED' | translate }}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
<mat-form-field class="formfield">
|
||||
<mat-label>{{ 'USER.PROFILE.USERNAME' | translate }}</mat-label>
|
||||
<input matInput formControlName="userName" required />
|
||||
<mat-error *ngIf="userName?.invalid && userName?.errors?.required">
|
||||
{{ 'USER.VALIDATION.REQUIRED' | translate }}
|
||||
</mat-error>
|
||||
<mat-error *ngIf="userName?.invalid && userName?.errors?.noEmailValidator">
|
||||
{{ 'USER.VALIDATION.NOEMAIL' | translate }}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
<mat-form-field class="formfield">
|
||||
<mat-label>{{ 'USER.PROFILE.FIRSTNAME' | translate }}</mat-label>
|
||||
<input matInput formControlName="firstName" required />
|
||||
<mat-error *ngIf="firstName?.invalid && firstName?.errors?.required">
|
||||
{{ 'USER.VALIDATION.REQUIRED' | translate }}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
<mat-form-field class="formfield">
|
||||
<mat-label>{{ 'USER.PROFILE.LASTNAME' | translate }}</mat-label>
|
||||
<input matInput formControlName="lastName" required />
|
||||
<mat-error *ngIf="lastName?.invalid && lastName?.errors?.required">
|
||||
{{ 'USER.VALIDATION.REQUIRED' | translate }}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
<mat-form-field class="formfield">
|
||||
<mat-label>{{ 'USER.PROFILE.NICKNAME' | translate }}</mat-label>
|
||||
<input matInput formControlName="nickName" />
|
||||
<mat-error *ngIf="nickName?.invalid && nickName?.errors?.required">
|
||||
{{ 'USER.VALIDATION.REQUIRED' | translate }}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<p class="section">{{ 'USER.CREATE.GENDERLANGSECTION' | translate }}</p>
|
||||
|
||||
<mat-form-field class="formfield">
|
||||
<mat-label>{{ 'USER.PROFILE.GENDER' | translate }}</mat-label>
|
||||
<mat-select formControlName="gender">
|
||||
<mat-option *ngFor="let gender of genders" [value]="gender">
|
||||
{{ 'GENDERS.'+gender | translate }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
<mat-error *ngIf="gender?.invalid && gender?.errors?.required">
|
||||
{{ 'USER.VALIDATION.REQUIRED' | translate }}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
<mat-form-field class="formfield">
|
||||
<mat-label>{{ 'USER.PROFILE.PREFERRED_LANGUAGE' | translate }}</mat-label>
|
||||
<mat-select formControlName="preferredLanguage">
|
||||
<mat-option *ngFor="let language of languages" [value]="language">
|
||||
{{ 'LANGUAGES.'+language | translate }}
|
||||
</mat-option>
|
||||
<mat-error *ngIf="preferredLanguage?.invalid && preferredLanguage?.errors?.required">
|
||||
{{ 'USER.VALIDATION.REQUIRED' | translate }}
|
||||
</mat-error>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<p class="section">{{ 'USER.CREATE.ADDRESSANDPHONESECTION' | translate }}</p>
|
||||
|
||||
<mat-form-field class="formfield">
|
||||
<mat-label>{{ 'USER.PROFILE.PHONE' | translate }}</mat-label>
|
||||
<input matInput formControlName="phone" />
|
||||
<mat-error *ngIf="phone?.invalid && phone?.errors?.required">
|
||||
{{ 'USER.VALIDATION.REQUIRED' | translate }}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
<!-- <mat-form-field class="formfield">
|
||||
<mat-label>{{ 'USER.ADDRESS.STREET' | translate }}</mat-label>
|
||||
<input matInput formControlName="streetAddress" />
|
||||
<mat-error *ngIf="streetAddress?.invalid && streetAddress?.errors?.required">
|
||||
{{ 'USER.VALIDATION.REQUIRED' | translate }}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
<mat-form-field class="formfield">
|
||||
<mat-label>{{ 'USER.ADDRESS.POSTAL_CODE' | translate }}</mat-label>
|
||||
<input matInput formControlName="postalCode" />
|
||||
<mat-error *ngIf="postalCode?.invalid && postalCode?.errors?.required">
|
||||
{{ 'USER.VALIDATION.REQUIRED' | translate }}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
<mat-form-field class="formfield">
|
||||
<mat-label>{{ 'USER.ADDRESS.LOCALITY' | translate }}</mat-label>
|
||||
<input matInput formControlName="locality" />
|
||||
<mat-error *ngIf="locality?.invalid && locality?.errors?.required">
|
||||
{{ 'USER.VALIDATION.REQUIRED' | translate }}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
<mat-form-field class="formfield">
|
||||
<mat-label>{{ 'USER.ADDRESS.REGION' | translate }}</mat-label>
|
||||
<input matInput formControlName="region" />
|
||||
<mat-error *ngIf="region?.invalid && region?.errors?.required">
|
||||
{{ 'USER.VALIDATION.REQUIRED' | translate }}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
<mat-form-field class="formfield">
|
||||
<mat-label>{{ 'USER.ADDRESS.COUNTRY' | translate }}</mat-label>
|
||||
<input matInput formControlName="country" />
|
||||
<mat-error *ngIf="country?.invalid && country?.errors?.required">
|
||||
{{ 'USER.VALIDATION.REQUIRED' | translate }}
|
||||
</mat-error>
|
||||
</mat-form-field> -->
|
||||
</div>
|
||||
<div class="btn-container">
|
||||
<button color="accent" [disabled]="userForm.invalid" type="submit"
|
||||
mat-raised-button>{{ 'ACTIONS.CREATE' | translate }}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,82 @@
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
padding-bottom: 3rem;
|
||||
|
||||
.left {
|
||||
width: 100px;
|
||||
display: flex;
|
||||
padding: 1rem;
|
||||
justify-content: center;
|
||||
|
||||
a {
|
||||
margin-top: .2rem;
|
||||
}
|
||||
}
|
||||
|
||||
.right {
|
||||
flex: 1;
|
||||
.head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid #ffffff20;
|
||||
flex-wrap: wrap;
|
||||
|
||||
a {
|
||||
display: block;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.desc {
|
||||
width: 100%;
|
||||
display: block;
|
||||
font-size: .9rem;
|
||||
color: #8795a1;
|
||||
}
|
||||
}
|
||||
|
||||
.form {
|
||||
padding-top: 1rem;
|
||||
|
||||
.btn-container {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
button {
|
||||
margin-top: 3rem;
|
||||
display: block;
|
||||
padding: 0.5rem 4rem;
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
margin: 0 -.5rem;
|
||||
|
||||
.section {
|
||||
padding: .5rem;
|
||||
flex-basis: 100%;
|
||||
color: #8795a1;
|
||||
font-size: .9rem;
|
||||
}
|
||||
|
||||
&.nowrap{
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
mat-form-field {
|
||||
flex: 1 0 33%;
|
||||
margin: 0 .5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { UserCreateComponent } from './user-create.component';
|
||||
|
||||
describe('UserCreateComponent', () => {
|
||||
let component: UserCreateComponent;
|
||||
let fixture: ComponentFixture<UserCreateComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [UserCreateComponent],
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(UserCreateComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,141 @@
|
||||
import { Component, OnDestroy } from '@angular/core';
|
||||
import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { Router } from '@angular/router';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { CreateUserRequest, Gender, User } from 'src/app/proto/generated/management_pb';
|
||||
import { MgmtUserService } from 'src/app/services/mgmt-user.service';
|
||||
import { OrgService } from 'src/app/services/org.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
function noEmailValidator(c: AbstractControl): any {
|
||||
const EMAIL_REGEXP: RegExp = /^((?!@).)*$/gm;
|
||||
if (!c.parent || !c) {
|
||||
return;
|
||||
}
|
||||
const username = c.parent.get('userName');
|
||||
|
||||
if (!username) {
|
||||
return;
|
||||
}
|
||||
|
||||
return EMAIL_REGEXP.test(username.value) ? null : {
|
||||
noEmailValidator: {
|
||||
valid: false,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-user-create',
|
||||
templateUrl: './user-create.component.html',
|
||||
styleUrls: ['./user-create.component.scss'],
|
||||
})
|
||||
export class UserCreateComponent implements OnDestroy {
|
||||
public user: CreateUserRequest.AsObject = new CreateUserRequest().toObject();
|
||||
public genders: Gender[] = [Gender.GENDER_FEMALE, Gender.GENDER_MALE, Gender.GENDER_UNSPECIFIED];
|
||||
public languages: string[] = ['de', 'en'];
|
||||
public userForm!: FormGroup;
|
||||
|
||||
private sub: Subscription = new Subscription();
|
||||
|
||||
public userLoginMustBeDomain: boolean = false;
|
||||
|
||||
constructor(
|
||||
private router: Router,
|
||||
private toast: ToastService,
|
||||
public userService: MgmtUserService,
|
||||
private fb: FormBuilder,
|
||||
private orgService: OrgService,
|
||||
) {
|
||||
this.orgService.GetMyOrgIamPolicy().then((iampolicy) => {
|
||||
this.userLoginMustBeDomain = iampolicy.toObject().userLoginMustBeDomain;
|
||||
this.initForm();
|
||||
}).catch(error => {
|
||||
console.error(error);
|
||||
this.initForm();
|
||||
});
|
||||
}
|
||||
|
||||
private initForm(): void {
|
||||
this.userForm = this.fb.group({
|
||||
email: ['', [Validators.required, Validators.email]],
|
||||
userName: ['',
|
||||
[
|
||||
Validators.required,
|
||||
Validators.minLength(2),
|
||||
this.userLoginMustBeDomain ? noEmailValidator : Validators.email,
|
||||
],
|
||||
],
|
||||
firstName: ['', Validators.required],
|
||||
lastName: ['', Validators.required],
|
||||
nickName: [''],
|
||||
gender: [Gender.GENDER_UNSPECIFIED],
|
||||
preferredLanguage: [''],
|
||||
phone: [''],
|
||||
streetAddress: [''],
|
||||
postalCode: [''],
|
||||
locality: [''],
|
||||
region: [''],
|
||||
country: [''],
|
||||
});
|
||||
}
|
||||
|
||||
public createUser(): void {
|
||||
this.user = this.userForm.value;
|
||||
|
||||
this.userService
|
||||
.CreateUser(this.user)
|
||||
.then((data: User) => {
|
||||
this.toast.showInfo('USER.TOAST.CREATED', true);
|
||||
this.router.navigate(['users', data.getId()]);
|
||||
})
|
||||
.catch(data => {
|
||||
this.toast.showError(data.message);
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
|
||||
this.sub.unsubscribe();
|
||||
}
|
||||
|
||||
public get email(): AbstractControl | null {
|
||||
return this.userForm.get('email');
|
||||
}
|
||||
public get userName(): AbstractControl | null {
|
||||
return this.userForm.get('userName');
|
||||
}
|
||||
public get firstName(): AbstractControl | null {
|
||||
return this.userForm.get('firstName');
|
||||
}
|
||||
public get lastName(): AbstractControl | null {
|
||||
return this.userForm.get('lastName');
|
||||
}
|
||||
public get nickName(): AbstractControl | null {
|
||||
return this.userForm.get('nickName');
|
||||
}
|
||||
public get gender(): AbstractControl | null {
|
||||
return this.userForm.get('gender');
|
||||
}
|
||||
public get preferredLanguage(): AbstractControl | null {
|
||||
return this.userForm.get('preferredLanguage');
|
||||
}
|
||||
public get phone(): AbstractControl | null {
|
||||
return this.userForm.get('phone');
|
||||
}
|
||||
public get streetAddress(): AbstractControl | null {
|
||||
return this.userForm.get('streetAddress');
|
||||
}
|
||||
public get postalCode(): AbstractControl | null {
|
||||
return this.userForm.get('postalCode');
|
||||
}
|
||||
public get locality(): AbstractControl | null {
|
||||
return this.userForm.get('locality');
|
||||
}
|
||||
public get region(): AbstractControl | null {
|
||||
return this.userForm.get('region');
|
||||
}
|
||||
public get country(): AbstractControl | null {
|
||||
return this.userForm.get('country');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
|
||||
import { UserCreateRoutingModule } from './user-create-routing.module';
|
||||
import { UserCreateComponent } from './user-create.component';
|
||||
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [UserCreateComponent],
|
||||
imports: [
|
||||
UserCreateRoutingModule,
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
MatInputModule,
|
||||
MatFormFieldModule,
|
||||
MatSelectModule,
|
||||
MatButtonModule,
|
||||
MatIconModule,
|
||||
MatProgressSpinnerModule,
|
||||
MatCheckboxModule,
|
||||
MatTooltipModule,
|
||||
TranslateModule,
|
||||
],
|
||||
})
|
||||
export class UserCreateModule { }
|
||||
Reference in New Issue
Block a user