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:
Max Peintner
2020-07-13 13:38:49 +02:00
committed by GitHub
parent d8eef34a37
commit b628baeb1a
193 changed files with 967 additions and 500 deletions
@@ -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 { }