fix(console): formfield changes and ux (#1020)

* cnsl-formfield, cnsl-label, cnsl-input, ch prefix

* create user cnsl-fields, add cnsl-error

* replace user forms with cnsl-fields

* replace all other occurrences of mat-form-field with cnsl-fields

* improve hover, active, focus behavior, headlin styling

* user-grant refactor

* style cleanup

* stylelint

* fix detail layout flexstart, user table header

* input dir

* error, input directives instead components, on blur handler

* error directive, formfield content children

* formfield control directive

* use new field for new contact dialog

* formfield hint, error directive, scss

* hint or error binding, animation after view init

* avatar changes, policy header

* style lint

* lint

* input control within formfield, errorstatematcher

* use matformcontrol to merge with selects

* member table min width, lint

* lint

* implement from matformcontrol insteat custom, add selector

* find error

* add focus listener

* labelfloat

* fix input directive

* renaming

* rm logs

* user search overflow, org create i18n, meta cleanup

* sidenav shadow

* redirect to org table

* lint

* lint test files

* remove usage of formfieldmodule, use input module, layout optim

* replace formfieldmodule usage with inputmodule

* lint input dir, scss

* user grant, user table optimisations

* reorganize sidenav, project table empty, tooltips, avatar ch

* user grant tooltips, form field error directive accessor

* checkbox overflow fix, table dateblock, i18n tooltips

* app placeholder, i18n, optimize mobile layout

* change refresh table order, i18n, redirect chips

* stylelint

* chore(deps-dev): bump stylelint from 13.7.2 to 13.8.0 in /console (#1007)

* fix: add project validation (#996)

* fix: project add validation (#997)

* fix: add project validation

* fix: removed loop

* chore(deps-dev): bump stylelint from 13.7.2 to 13.8.0 in /console

Bumps [stylelint](https://github.com/stylelint/stylelint) from 13.7.2 to 13.8.0.
- [Release notes](https://github.com/stylelint/stylelint/releases)
- [Changelog](https://github.com/stylelint/stylelint/blob/master/CHANGELOG.md)
- [Commits](https://github.com/stylelint/stylelint/compare/13.7.2...13.8.0)

Signed-off-by: dependabot[bot] <[email protected]>

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

* chore(deps-dev): bump @angular/cli from 11.0.1 to 11.0.2 in /console (#1006)

* fix: add project validation (#996)

* fix: project add validation (#997)

* fix: add project validation

* fix: removed loop

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

Bumps [@angular/cli](https://github.com/angular/angular-cli) from 11.0.1 to 11.0.2.
- [Release notes](https://github.com/angular/angular-cli/releases)
- [Commits](https://github.com/angular/angular-cli/compare/v11.0.1...v11.0.2)

Signed-off-by: dependabot[bot] <[email protected]>

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

* chore(deps): bump google-proto-files from 2.2.0 to 2.3.0 in /console (#1005)

* fix: add project validation (#996)

* fix: project add validation (#997)

* fix: add project validation

* fix: removed loop

* chore(deps): bump google-proto-files from 2.2.0 to 2.3.0 in /console

Bumps [google-proto-files](https://github.com/googleapis/nodejs-proto-files) from 2.2.0 to 2.3.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/v2.2.0...v2.3.0)

Signed-off-by: dependabot[bot] <[email protected]>

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

* chore(deps-dev): bump @types/node from 14.14.6 to 14.14.9 in /console (#1004)

* fix: add project validation (#996)

* fix: project add validation (#997)

* fix: add project validation

* fix: removed loop

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

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

Signed-off-by: dependabot[bot] <[email protected]>

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

* chore(deps-dev): bump @angular/language-service from 11.0.0 to 11.0.2 in /console (#999)

* fix: add project validation (#996)

* fix: project add validation (#997)

* fix: add project validation

* fix: removed loop

* 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.0.0 to 11.0.2.
- [Release notes](https://github.com/angular/angular/releases)
- [Changelog](https://github.com/angular/angular/blob/master/CHANGELOG.md)
- [Commits](https://github.com/angular/angular/commits/11.0.2/packages/language-service)

Signed-off-by: dependabot[bot] <[email protected]>

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

* chore(deps-dev): bump @angular-devkit/build-angular from 0.1100.1 to 0.1100.2 in /console (#1003)

* fix: add project validation (#996)

* fix: project add validation (#997)

* fix: add project validation

* fix: removed loop

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

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

Signed-off-by: dependabot[bot] <[email protected]>

Co-authored-by: Silvan <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Max Peintner <[email protected]>

* chore(deps-dev): bump prettier from 2.1.2 to 2.2.0 in /console (#1002)

* fix: add project validation (#996)

* fix: project add validation (#997)

* fix: add project validation

* fix: removed loop

* chore(deps-dev): bump prettier from 2.1.2 to 2.2.0 in /console

Bumps [prettier](https://github.com/prettier/prettier) from 2.1.2 to 2.2.0.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/master/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/2.1.2...2.2.0)

Signed-off-by: dependabot[bot] <[email protected]>

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

* chore(deps-dev): bump @types/jasmine from 3.6.1 to 3.6.2 in /console (#1001)

* fix: add project validation (#996)

* fix: project add validation (#997)

* fix: add project validation

* fix: removed loop

* chore(deps-dev): bump @types/jasmine from 3.6.1 to 3.6.2 in /console

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

Signed-off-by: dependabot[bot] <[email protected]>

Co-authored-by: Silvan <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Max Peintner <[email protected]>

* chore(deps): bump file-saver from 2.0.2 to 2.0.5 in /console (#1000)

* fix: add project validation (#996)

* fix: project add validation (#997)

* fix: add project validation

* fix: removed loop

* chore(deps): bump file-saver from 2.0.2 to 2.0.5 in /console

Bumps [file-saver](https://github.com/eligrey/FileSaver.js) from 2.0.2 to 2.0.5.
- [Release notes](https://github.com/eligrey/FileSaver.js/releases)
- [Changelog](https://github.com/eligrey/FileSaver.js/blob/master/CHANGELOG.md)
- [Commits](https://github.com/eligrey/FileSaver.js/commits)

Signed-off-by: dependabot[bot] <[email protected]>

Co-authored-by: Silvan <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Max Peintner <[email protected]>

* chore(deps-dev): bump jasmine-spec-reporter in /console (#974)

Bumps [jasmine-spec-reporter](https://github.com/bcaudan/jasmine-spec-reporter) from 5.0.2 to 6.0.0.
- [Release notes](https://github.com/bcaudan/jasmine-spec-reporter/releases)
- [Changelog](https://github.com/bcaudan/jasmine-spec-reporter/blob/master/CHANGELOG.md)
- [Commits](https://github.com/bcaudan/jasmine-spec-reporter/compare/v5.0.2...v6.0.0)

Signed-off-by: dependabot[bot] <[email protected]>

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

* input field border improvements, primary color

* redirect form, conventional ux

* redirects inputs on app create

* fix dynamic suffix padding, user grant layout, lint

* input style accessor, contact view optim, ext idp, card fixes

* hide error on untouched formfield, border red on error touched

* fix change detector

* change, user, form validation, delete refresh

* safari font weight adjustment, change refresh emitter

* key validation

* remove tabls layout from projects

* app changes, refactor project, app state, meta layout

* add key dialog validation, startdate, rever change tabs

* stylelint

* Update console/src/assets/i18n/en.json

Co-authored-by: Florian Forster <[email protected]>

* Update console/src/assets/i18n/en.json

Co-authored-by: Florian Forster <[email protected]>

* Update console/src/assets/i18n/de.json

Co-authored-by: Florian Forster <[email protected]>

* Update console/src/assets/i18n/de.json

Co-authored-by: Florian Forster <[email protected]>

* Update console/src/assets/i18n/de.json

Co-authored-by: Florian Forster <[email protected]>

* Update console/src/assets/i18n/de.json

Co-authored-by: Florian Forster <[email protected]>

* Update console/src/assets/i18n/de.json

Co-authored-by: Florian Forster <[email protected]>

* Update console/src/assets/i18n/de.json

Co-authored-by: Florian Forster <[email protected]>

* Update console/src/assets/i18n/en.json

Co-authored-by: Florian Forster <[email protected]>

* Update console/src/assets/i18n/en.json

Co-authored-by: Florian Forster <[email protected]>

* finnaly fix fckntimestamp picker

* optional key expiry

* small state badge

* Update console/src/assets/i18n/de.json

Co-authored-by: Florian Forster <[email protected]>

* Update console/src/assets/i18n/de.json

Co-authored-by: Florian Forster <[email protected]>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Silvan <[email protected]>
Co-authored-by: Florian Forster <[email protected]>
This commit is contained in:
Max Peintner
2020-11-25 16:53:14 +01:00
committed by GitHub
co-authored by dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Florian Forster dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Silvan
parent 42f50de790
commit ae26999834
191 changed files with 3622 additions and 2109 deletions
@@ -4,27 +4,27 @@
<form *ngIf="userForm" [formGroup]="userForm" (ngSubmit)="createUser()" class="form">
<div class="content">
<mat-form-field class="formfield">
<mat-label>{{ 'USER.MACHINE.USERNAME' | translate }}</mat-label>
<input matInput formControlName="userName" required />
<mat-error *ngIf="userName?.invalid && userName?.errors?.required">
<cnsl-form-field class="formfield">
<cnsl-label>{{ 'USER.MACHINE.USERNAME' | translate }}</cnsl-label>
<input cnslInput formControlName="userName" required />
<span cnsl-error *ngIf="userName?.invalid && userName?.errors?.required">
{{ 'USER.VALIDATION.REQUIRED' | translate }}
</mat-error>
<mat-error *ngIf="userName?.invalid && userName?.errors?.noEmailValidator">
</span>
<span cnsl-error *ngIf="userName?.invalid && userName?.errors?.noEmailValidator">
{{ 'USER.VALIDATION.NOEMAIL' | translate }}
</mat-error>
</mat-form-field>
<mat-form-field class="formfield">
<mat-label>{{ 'USER.MACHINE.NAME' | translate }}</mat-label>
<input matInput formControlName="name" required />
<mat-error *ngIf="name?.invalid && name?.errors?.required">
</span>
</cnsl-form-field>
<cnsl-form-field class="formfield">
<cnsl-label>{{ 'USER.MACHINE.NAME' | translate }}</cnsl-label>
<input cnslInput formControlName="name" required />
<span cnsl-error *ngIf="name?.invalid && name?.errors?.required">
{{ 'USER.VALIDATION.REQUIRED' | translate }}
</mat-error>
</mat-form-field>
<mat-form-field class="formfield">
<mat-label>{{ 'USER.MACHINE.DESCRIPTION' | translate }}</mat-label>
<input matInput formControlName="description" />
</mat-form-field>
</span>
</cnsl-form-field>
<cnsl-form-field class="formfield">
<cnsl-label>{{ 'USER.MACHINE.DESCRIPTION' | translate }}</cnsl-label>
<input cnslInput formControlName="description" />
</cnsl-form-field>
</div>
<div class="btn-container">
<button color="primary" [disabled]="userForm.invalid" type="submit"
@@ -3,15 +3,14 @@ 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 { MatProgressBarModule } from '@angular/material/progress-bar';
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 { DetailLayoutModule } from 'src/app/modules/detail-layout/detail-layout.module';
import { InputModule } from 'src/app/modules/input/input.module';
import { UserCreateMachineRoutingModule } from './user-create-machine-routing.module';
import { UserCreateMachineComponent } from './user-create-machine.component';
@@ -25,8 +24,6 @@ import { UserCreateMachineComponent } from './user-create-machine.component';
CommonModule,
FormsModule,
ReactiveFormsModule,
MatInputModule,
MatFormFieldModule,
MatSelectModule,
MatButtonModule,
MatIconModule,
@@ -36,6 +33,7 @@ import { UserCreateMachineComponent } from './user-create-machine.component';
MatTooltipModule,
TranslateModule,
DetailLayoutModule,
InputModule,
],
})
export class UserCreateMachineModule { }
@@ -5,83 +5,87 @@
<form *ngIf="userForm" [formGroup]="userForm" (ngSubmit)="createUser()" class="form">
<div class="content">
<p class="section">{{ 'USER.CREATE.NAMEANDEMAILSECTION' | translate }}</p>
<mat-form-field class="formfield" appearance="fill">
<mat-label>{{ 'USER.PROFILE.EMAIL' | translate }}</mat-label>
<input matInput formControlName="email" required />
<mat-error *ngIf="email?.invalid && email?.errors?.required">
<cnsl-form-field class="formfield" appearance="fill">
<cnsl-label>{{ 'USER.PROFILE.EMAIL' | translate }}*</cnsl-label>
<input cnslInput matRipple formControlName="email" required />
<span cnsl-error *ngIf="email?.invalid && !email?.errors?.required">
{{ 'USER.VALIDATION.NOTANEMAIL' | translate }}
</span>
<span cnsl-error *ngIf="email?.invalid && email?.errors?.required">
{{ 'USER.VALIDATION.REQUIRED' | translate }}
</mat-error>
</mat-form-field>
<mat-form-field class="formfield" appearance="fill">
<mat-label>{{ 'USER.PROFILE.USERNAME' | translate }}</mat-label>
<input matInput formControlName="userName" required />
<span *ngIf="envSuffix" matSuffix>{{envSuffix}}</span>
</span>
</cnsl-form-field>
<cnsl-form-field class="formfield" appearance="fill">
<cnsl-label>{{ 'USER.PROFILE.USERNAME' | translate }}*</cnsl-label>
<input cnslInput formControlName="userName" required
[ngStyle]="{'padding-right': suffixPadding ? suffixPadding : '10px'}" />
<span #suffix *ngIf="envSuffixLabel" cnslSuffix (click)="logsuff()">{{envSuffixLabel}}</span>
<mat-error *ngIf="userName?.invalid && userName?.errors?.required">
<span cnsl-error *ngIf="userName?.invalid && userName?.errors?.required">
{{ 'USER.VALIDATION.REQUIRED' | translate }}
</mat-error>
<mat-error *ngIf="userName?.invalid && userName?.errors?.noEmailValidator">
</span>
<span cnsl-error *ngIf="userName?.invalid && userName?.errors?.noEmailValidator">
{{ 'USER.VALIDATION.NOEMAIL' | translate }}
</mat-error>
</mat-form-field>
</span>
</cnsl-form-field>
</div>
<div class="content">
<mat-form-field class="formfield" appearance="fill">
<mat-label>{{ 'USER.PROFILE.FIRSTNAME' | translate }}</mat-label>
<input matInput formControlName="firstName" required />
<mat-error *ngIf="firstName?.invalid && firstName?.errors?.required">
<cnsl-form-field class="formfield" appearance="fill">
<cnsl-label>{{ 'USER.PROFILE.FIRSTNAME' | translate }}*</cnsl-label>
<input cnslInput formControlName="firstName" required />
<span cnsl-error *ngIf="firstName?.invalid && firstName?.errors?.required">
{{ 'USER.VALIDATION.REQUIRED' | translate }}
</mat-error>
</mat-form-field>
<mat-form-field class="formfield" appearance="fill">
<mat-label>{{ 'USER.PROFILE.LASTNAME' | translate }}</mat-label>
<input matInput formControlName="lastName" required />
<mat-error *ngIf="lastName?.invalid && lastName?.errors?.required">
</span>
</cnsl-form-field>
<cnsl-form-field class="formfield" appearance="fill">
<cnsl-label>{{ 'USER.PROFILE.LASTNAME' | translate }}*</cnsl-label>
<input cnslInput formControlName="lastName" required />
<span cnsl-error *ngIf="lastName?.invalid && lastName?.errors?.required">
{{ 'USER.VALIDATION.REQUIRED' | translate }}
</mat-error>
</mat-form-field>
<mat-form-field class="formfield" appearance="fill">
<mat-label>{{ 'USER.PROFILE.NICKNAME' | translate }}</mat-label>
<input matInput formControlName="nickName" />
<mat-error *ngIf="nickName?.invalid && nickName?.errors?.required">
</span>
</cnsl-form-field>
<cnsl-form-field class="formfield" appearance="fill">
<cnsl-label>{{ 'USER.PROFILE.NICKNAME' | translate }}</cnsl-label>
<input cnslInput formControlName="nickName" />
<span cnsl-error *ngIf="nickName?.invalid && nickName?.errors?.required">
{{ 'USER.VALIDATION.REQUIRED' | translate }}
</mat-error>
</mat-form-field>
</span>
</cnsl-form-field>
<p class="section">{{ 'USER.CREATE.GENDERLANGSECTION' | translate }}</p>
<mat-form-field class="formfield" appearance="fill">
<mat-label>{{ 'USER.PROFILE.GENDER' | translate }}</mat-label>
<cnsl-form-field class="formfield" appearance="fill">
<cnsl-label>{{ 'USER.PROFILE.GENDER' | translate }}</cnsl-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">
<span cnsl-error *ngIf="gender?.invalid && gender?.errors?.required">
{{ 'USER.VALIDATION.REQUIRED' | translate }}
</mat-error>
</mat-form-field>
<mat-form-field class="formfield" appearance="fill">
<mat-label>{{ 'USER.PROFILE.PREFERRED_LANGUAGE' | translate }}</mat-label>
</span>
</cnsl-form-field>
<cnsl-form-field class="formfield" appearance="fill">
<cnsl-label>{{ 'USER.PROFILE.PREFERRED_LANGUAGE' | translate }}</cnsl-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">
<span cnsl-error *ngIf="preferredLanguage?.invalid && preferredLanguage?.errors?.required">
{{ 'USER.VALIDATION.REQUIRED' | translate }}
</mat-error>
</span>
</mat-select>
</mat-form-field>
</cnsl-form-field>
<p class="section">{{ 'USER.CREATE.ADDRESSANDPHONESECTION' | translate }}</p>
<mat-form-field class="formfield" appearance="fill">
<mat-label>{{ 'USER.PROFILE.PHONE' | translate }}</mat-label>
<input matInput formControlName="phone" />
<mat-error *ngIf="phone?.invalid && phone?.errors?.required">
<cnsl-form-field class="formfield" appearance="fill">
<cnsl-label>{{ 'USER.PROFILE.PHONE' | translate }}</cnsl-label>
<input cnslInput formControlName="phone" />
<span cnsl-error *ngIf="phone?.invalid && phone?.errors?.required">
{{ 'USER.VALIDATION.REQUIRED' | translate }}
</mat-error>
</mat-form-field>
</span>
</cnsl-form-field>
</div>
<div class="btn-container">
<button color="primary" [disabled]="userForm.invalid" type="submit"
@@ -25,6 +25,8 @@
flex-basis: 100%;
color: var(--grey);
font-size: .9rem;
letter-spacing: .05em;
text-transform: uppercase;
}
.formfield {
@@ -1,4 +1,4 @@
import { Component, OnDestroy } from '@angular/core';
import { ChangeDetectorRef, Component, OnDestroy, ViewChild } from '@angular/core';
import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Router } from '@angular/router';
import { Subscription } from 'rxjs';
@@ -40,12 +40,13 @@ export class UserCreateComponent implements OnDestroy {
public genders: Gender[] = [Gender.GENDER_FEMALE, Gender.GENDER_MALE, Gender.GENDER_UNSPECIFIED];
public languages: string[] = ['de', 'en'];
public userForm!: FormGroup;
public envSuffixLabel: string = '';
private sub: Subscription = new Subscription();
public userLoginMustBeDomain: boolean = false;
public loading: boolean = false;
@ViewChild('suffix') public suffix!: any;
private primaryDomain!: OrgDomain.AsObject;
constructor(
@@ -53,6 +54,7 @@ export class UserCreateComponent implements OnDestroy {
private toast: ToastService,
private fb: FormBuilder,
private mgmtService: ManagementService,
private changeDetRef: ChangeDetectorRef,
) {
this.loading = true;
this.loadOrg();
@@ -60,10 +62,14 @@ export class UserCreateComponent implements OnDestroy {
this.userLoginMustBeDomain = iampolicy.toObject().userLoginMustBeDomain;
this.initForm();
this.loading = false;
this.envSuffixLabel = this.envSuffix();
this.changeDetRef.detectChanges();
}).catch(error => {
console.error(error);
this.initForm();
this.loading = false;
this.envSuffixLabel = this.envSuffix();
this.changeDetRef.detectChanges();
});
}
@@ -92,6 +98,11 @@ export class UserCreateComponent implements OnDestroy {
preferredLanguage: [''],
phone: [''],
});
}
public logsuff(): void {
console.log((this.suffix.nativeElement as HTMLElement), (this.suffix.nativeElement as HTMLElement).offsetWidth);
}
public createUser(): void {
@@ -123,7 +134,6 @@ export class UserCreateComponent implements OnDestroy {
}
ngOnDestroy(): void {
this.sub.unsubscribe();
}
@@ -167,11 +177,17 @@ export class UserCreateComponent implements OnDestroy {
return this.userForm.get('country');
}
public get envSuffix(): string {
private envSuffix(): string {
if (this.userLoginMustBeDomain && this.primaryDomain?.domain) {
return `@${this.primaryDomain.domain}`;
} else {
return '';
}
}
public get suffixPadding(): string | undefined {
if (this.suffix?.nativeElement.offsetWidth) {
return `${(this.suffix.nativeElement as HTMLElement).offsetWidth + 10}px`;
}
}
}
@@ -3,21 +3,19 @@ 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 { MatRippleModule } from '@angular/material/core';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatProgressBarModule } from '@angular/material/progress-bar';
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 { DetailLayoutModule } from 'src/app/modules/detail-layout/detail-layout.module';
import { InputModule } from 'src/app/modules/input/input.module';
import { UserCreateRoutingModule } from './user-create-routing.module';
import { UserCreateComponent } from './user-create.component';
@NgModule({
declarations: [UserCreateComponent],
imports: [
@@ -25,8 +23,6 @@ import { UserCreateComponent } from './user-create.component';
CommonModule,
FormsModule,
ReactiveFormsModule,
MatInputModule,
MatFormFieldModule,
MatSelectModule,
MatButtonModule,
MatIconModule,
@@ -36,6 +32,8 @@ import { UserCreateComponent } from './user-create.component';
MatTooltipModule,
TranslateModule,
DetailLayoutModule,
InputModule,
MatRippleModule,
],
})
export class UserCreateModule { }
@@ -56,7 +56,7 @@
<app-card *ngIf="user?.id" title="{{ 'GRANTS.USER.TITLE' | translate }}"
description="{{'GRANTS.USER.DESCRIPTION' | translate }}">
<app-user-grants [userId]="user.id" [context]="USERGRANTCONTEXT"
[displayedColumns]="['select', 'projectId', 'creationDate', 'changeDate', 'roleNamesList']"
[displayedColumns]="['select', 'projectId', 'dates', 'roleNamesList']"
[disableWrite]="((['user.grant.write$'] | hasRole) | async) == false"
[disableDelete]="((['user.grant.delete$'] | hasRole) | async) == false">
</app-user-grants>
@@ -74,6 +74,7 @@
<app-memberships [user]="user"></app-memberships>
</ng-template>
<app-changes class="changes" [changeType]="ChangeType.MYUSER" [id]="user.id"></app-changes>
<app-changes class="changes" [refresh]="refreshChanges$" [changeType]="ChangeType.MYUSER" [id]="user.id">
</app-changes>
</div>
</app-meta-layout>
@@ -1,4 +1,4 @@
import { Component, OnDestroy } from '@angular/core';
import { Component, EventEmitter, OnDestroy } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { TranslateService } from '@ngx-translate/core';
import { Subscription } from 'rxjs';
@@ -41,6 +41,7 @@ export class AuthUserDetailComponent implements OnDestroy {
public UserState: any = UserState;
public USERGRANTCONTEXT: UserGrantContext = UserGrantContext.USER;
public refreshChanges$: EventEmitter<void> = new EventEmitter();
constructor(
public translate: TranslateService,
@@ -53,6 +54,7 @@ export class AuthUserDetailComponent implements OnDestroy {
}
refreshUser(): void {
this.refreshChanges$.emit();
this.userService.GetMyUser().then(user => {
this.user = user.toObject();
this.loading = false;
@@ -86,6 +88,7 @@ export class AuthUserDetailComponent implements OnDestroy {
.then((data: UserProfile) => {
this.toast.showInfo('USER.TOAST.SAVED', true);
this.user = Object.assign(this.user, data.toObject());
this.refreshChanges$.emit();
})
.catch(error => {
this.toast.showError(error);
@@ -122,6 +125,7 @@ export class AuthUserDetailComponent implements OnDestroy {
public resendPhoneVerification(): void {
this.userService.ResendPhoneVerification().then(() => {
this.toast.showInfo('USER.TOAST.PHONEVERIFICATIONSENT', true);
this.refreshChanges$.emit();
}).catch(error => {
this.toast.showError(error);
});
@@ -130,6 +134,7 @@ export class AuthUserDetailComponent implements OnDestroy {
public resendEmailVerification(): void {
this.userService.ResendMyEmailVerificationMail().then(() => {
this.toast.showInfo('USER.TOAST.EMAILVERIFICATIONSENT', true);
this.refreshChanges$.emit();
}).catch(error => {
this.toast.showError(error);
});
@@ -3,10 +3,10 @@
</h1>
<p class="desc">{{'USER.CODEDIALOG.DESCRIPTION' | translate}}</p>
<div mat-dialog-content>
<mat-form-field class="formfield">
<mat-label>{{ 'USER.CODEDIALOG.CODE' | translate }}</mat-label>
<input matInput [(ngModel)]="code" />
</mat-form-field>
<cnsl-form-field class="formfield">
<cnsl-label>{{ 'USER.CODEDIALOG.CODE' | translate }}</cnsl-label>
<input cnslInput [(ngModel)]="code" />
</cnsl-form-field>
</div>
<div mat-dialog-actions class="action">
<button cdkFocusInitial color="primary" mat-button class="ok-button" (click)="closeDialog()">
@@ -5,10 +5,10 @@
<qrcode *ngIf="data" class="qrcode" [qrdata]="data" [width]="150" [errorCorrectionLevel]="'M'"></qrcode>
</div>
<mat-form-field class="form-field" label="Access Code" required="true">
<mat-label>Code</mat-label>
<input matInput [(ngModel)]="code" />
</mat-form-field>
<cnsl-form-field class="form-field" label="Access Code" required="true">
<cnsl-label>Code</cnsl-label>
<input cnslInput [(ngModel)]="code" />
</cnsl-form-field>
</div>
<div mat-dialog-actions class="action">
<button mat-button (click)="closeDialog()"><span translate>ACTIONS.CLOSE</span></button>
@@ -3,10 +3,10 @@
</h1>
<p class="desc">{{data.descriptionKey | translate}}</p>
<div mat-dialog-content>
<mat-form-field class="formfield">
<mat-label>{{data.labelKey | translate }}</mat-label>
<input matInput [(ngModel)]="value" />
</mat-form-field>
<cnsl-form-field class="formfield">
<cnsl-label>{{data.labelKey | translate }}</cnsl-label>
<input cnslInput [(ngModel)]="value" />
</cnsl-form-field>
</div>
<div mat-dialog-actions class="action">
<button cdkFocusInitial color="primary" mat-button class="ok-button" (click)="closeDialog()">
@@ -1,20 +1,20 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { CodeDialogComponent } from './code-dialog.component';
import { EditDialogComponent } from './edit-dialog.component';
describe('CodeDialogComponent', () => {
let component: CodeDialogComponent;
let fixture: ComponentFixture<CodeDialogComponent>;
let component: EditDialogComponent;
let fixture: ComponentFixture<EditDialogComponent>;
beforeEach(async(() => {
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [CodeDialogComponent],
declarations: [EditDialogComponent],
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(CodeDialogComponent);
fixture = TestBed.createComponent(EditDialogComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
@@ -3,10 +3,10 @@
</h1>
<p class="desc">{{'USER.SENDEMAILDIALOG.DESCRIPTION' | translate}}</p>
<div mat-dialog-content>
<mat-form-field class="formfield">
<mat-label>{{ 'USER.SENDEMAILDIALOG.NEWEMAIL' | translate }}</mat-label>
<input matInput [(ngModel)]="email" />
</mat-form-field>
<cnsl-form-field class="formfield">
<cnsl-label>{{ 'USER.SENDEMAILDIALOG.NEWEMAIL' | translate }}</cnsl-label>
<input cnslInput [(ngModel)]="email" />
</cnsl-form-field>
</div>
<div mat-dialog-actions class="action">
<button color="primary" mat-button class="ok-button" (click)="closeDialog()">
@@ -1,20 +1,20 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { CodeDialogComponent } from './code-dialog.component';
import { ResendEmailDialogComponent } from './resend-email-dialog.component';
describe('CodeDialogComponent', () => {
let component: CodeDialogComponent;
let fixture: ComponentFixture<CodeDialogComponent>;
describe('ResendEmailDialogComponent', () => {
let component: ResendEmailDialogComponent;
let fixture: ComponentFixture<ResendEmailDialogComponent>;
beforeEach(async(() => {
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [CodeDialogComponent],
declarations: [ResendEmailDialogComponent],
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(CodeDialogComponent);
fixture = TestBed.createComponent(ResendEmailDialogComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
@@ -34,7 +34,8 @@
</div>
<div class="right">
<button [disabled]="!canWrite" (click)="openEditDialog(EditDialogType.EMAIL)" mat-icon-button>
<button matTooltip="{{'ACTIONS.EDIT' | translate}}" [disabled]="!canWrite"
(click)="openEditDialog(EditDialogType.EMAIL)" mat-icon-button>
<i class="las la-edit"></i>
</button>
</div>
@@ -8,6 +8,7 @@
justify-content: space-between;
align-items: center;
padding: .5rem;
padding-right: 0;
border-bottom: 1px solid #ffffff20;
flex-wrap: wrap;
@@ -1,26 +1,26 @@
<form [formGroup]="machineForm" *ngIf="machineForm" (ngSubmit)="submitForm()">
<div class="content">
<mat-form-field class="formfield">
<mat-label>{{ 'USER.MACHINE.USERNAME' | translate }}</mat-label>
<input matInput formControlName="userName" required />
<mat-error *ngIf="userName?.invalid && userName?.errors?.required">
<cnsl-form-field class="formfield">
<cnsl-label>{{ 'USER.MACHINE.USERNAME' | translate }}</cnsl-label>
<input cnslInput formControlName="userName" required />
<span cnsl-error *ngIf="userName?.invalid && userName?.errors?.required">
{{ 'USER.VALIDATION.REQUIRED' | translate }}
</mat-error>
<mat-error *ngIf="userName?.invalid && userName?.errors?.noEmailValidator">
</span>
<span cnsl-error *ngIf="userName?.invalid && userName?.errors?.noEmailValidator">
{{ 'USER.VALIDATION.NOEMAIL' | translate }}
</mat-error>
</mat-form-field>
<mat-form-field class="formfield">
<mat-label>{{ 'USER.MACHINE.NAME' | translate }}</mat-label>
<input matInput formControlName="name" required />
<mat-error *ngIf="name?.invalid && name?.errors?.required">
</span>
</cnsl-form-field>
<cnsl-form-field class="formfield">
<cnsl-label>{{ 'USER.MACHINE.NAME' | translate }}</cnsl-label>
<input cnslInput formControlName="name" required />
<span cnsl-error *ngIf="name?.invalid && name?.errors?.required">
{{ 'USER.VALIDATION.REQUIRED' | translate }}
</mat-error>
</mat-form-field>
<mat-form-field class="formfield">
<mat-label>{{ 'USER.MACHINE.DESCRIPTION' | translate }}</mat-label>
<input matInput formControlName="description" />
</mat-form-field>
</span>
</cnsl-form-field>
<cnsl-form-field class="formfield">
<cnsl-label>{{ 'USER.MACHINE.DESCRIPTION' | translate }}</cnsl-label>
<input cnslInput formControlName="description" />
</cnsl-form-field>
</div>
<div class="btn-container">
<button type="submit" color="primary" mat-raised-button>{{ 'ACTIONS.SAVE' | translate }}</button>
@@ -2,11 +2,10 @@ import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatSelectModule } from '@angular/material/select';
import { TranslateModule } from '@ngx-translate/core';
import { InputModule } from 'src/app/modules/input/input.module';
import { DetailFormMachineComponent } from './detail-form-machine.component';
@@ -20,8 +19,7 @@ import { DetailFormMachineComponent } from './detail-form-machine.component';
FormsModule,
ReactiveFormsModule,
TranslateModule,
MatFormFieldModule,
MatInputModule,
InputModule,
MatSelectModule,
MatButtonModule,
MatIconModule,
@@ -1,37 +1,37 @@
<form [formGroup]="profileForm" *ngIf="profileForm" (ngSubmit)="submitForm()">
<div class="content">
<mat-form-field class="formfield" appearance="outline">
<mat-label>{{ 'USER.PROFILE.USERNAME' | translate }}</mat-label>
<input matInput formControlName="userName" />
</mat-form-field>
<mat-form-field class="formfield" appearance="outline">
<mat-label>{{ 'USER.PROFILE.FIRSTNAME' | translate }}</mat-label>
<input matInput formControlName="firstName" />
</mat-form-field>
<mat-form-field class="formfield" appearance="outline">
<mat-label>{{ 'USER.PROFILE.LASTNAME' | translate }}</mat-label>
<input matInput formControlName="lastName" />
</mat-form-field>
<mat-form-field class="formfield" appearance="outline">
<mat-label>{{ 'USER.PROFILE.NICKNAME' | translate }}</mat-label>
<input matInput formControlName="nickName" />
</mat-form-field>
<mat-form-field class="formfield" appearance="outline">
<mat-label>{{ 'USER.PROFILE.GENDER' | translate }}</mat-label>
<cnsl-form-field class="formfield">
<cnsl-label>{{ 'USER.PROFILE.USERNAME' | translate }}</cnsl-label>
<input cnslInput formControlName="userName" />
</cnsl-form-field>
<cnsl-form-field class="formfield">
<cnsl-label>{{ 'USER.PROFILE.FIRSTNAME' | translate }}</cnsl-label>
<input cnslInput formControlName="firstName" />
</cnsl-form-field>
<cnsl-form-field class="formfield">
<cnsl-label>{{ 'USER.PROFILE.LASTNAME' | translate }}</cnsl-label>
<input cnslInput formControlName="lastName" />
</cnsl-form-field>
<cnsl-form-field class="formfield">
<cnsl-label>{{ 'USER.PROFILE.NICKNAME' | translate }}</cnsl-label>
<input cnslInput formControlName="nickName" />
</cnsl-form-field>
<cnsl-form-field class="formfield">
<cnsl-label>{{ 'USER.PROFILE.GENDER' | translate }}</cnsl-label>
<mat-select formControlName="gender">
<mat-option *ngFor="let gender of genders" [value]="gender">
{{ 'GENDERS.'+gender | translate }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field class="formfield" appearance="outline">
<mat-label>{{ 'USER.PROFILE.PREFERRED_LANGUAGE' | translate }}</mat-label>
</cnsl-form-field>
<cnsl-form-field class="formfield">
<cnsl-label>{{ 'USER.PROFILE.PREFERRED_LANGUAGE' | translate }}</cnsl-label>
<mat-select formControlName="preferredLanguage">
<mat-option *ngFor="let language of languages" [value]="language">
{{ 'LANGUAGES.'+language | translate }}
</mat-option>
</mat-select>
</mat-form-field>
</cnsl-form-field>
</div>
<div class="btn-container">
<button [disabled]="disabled" class="submit-button" type="submit" color="primary"
@@ -2,15 +2,13 @@ import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatSelectModule } from '@angular/material/select';
import { TranslateModule } from '@ngx-translate/core';
import { InputModule } from 'src/app/modules/input/input.module';
import { DetailFormComponent } from './detail-form.component';
@NgModule({
declarations: [
DetailFormComponent,
@@ -20,12 +18,11 @@ import { DetailFormComponent } from './detail-form.component';
FormsModule,
ReactiveFormsModule,
TranslateModule,
MatFormFieldModule,
MatInputModule,
MatSelectModule,
MatButtonModule,
MatIconModule,
TranslateModule,
InputModule,
],
exports: [
DetailFormComponent,
@@ -24,4 +24,14 @@
tr {
outline: none;
button {
visibility: hidden;
}
&:hover {
button {
visibility: visible;
}
}
}
@@ -2,28 +2,32 @@
<div mat-dialog-content>
<p class="desc"> {{'USER.MACHINE.ADD.DESCRIPTION' | translate}}</p>
<mat-form-field class="form-field" appearance="outline">
<mat-label>{{'USER.MACHINE.TYPE' | translate}}</mat-label>
<cnsl-form-field class="form-field" appearance="outline">
<cnsl-label>{{'USER.MACHINE.TYPE' | translate}}</cnsl-label>
<mat-select [(ngModel)]="type">
<mat-option *ngFor="let t of types" [value]="t">
{{'USER.MACHINE.KEYTYPES.'+t | translate}}
</mat-option>
</mat-select>
</mat-form-field>
</cnsl-form-field>
<mat-form-field class="form-field" appearance="outline">
<mat-label>{{'USER.MACHINE.CHOOSEEXPIRY' | translate}}</mat-label>
<input matInput [matDatepicker]="picker" [(ngModel)]="date">
<mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
<mat-datepicker #picker></mat-datepicker>
</mat-form-field>
<cnsl-form-field class="form-field" appearance="outline">
<cnsl-label>{{'USER.MACHINE.CHOOSEEXPIRY' | translate}} (optional)</cnsl-label>
<input cnslInput [matDatepicker]="picker" [min]="startDate" [formControl]="dateControl">
<mat-datepicker-toggle style="top: 0;" cnslSuffix [for]="picker"></mat-datepicker-toggle>
<mat-datepicker #picker startView="year" [startAt]="startDate"></mat-datepicker>
<span cnsl-error *ngIf="dateControl?.errors?.matDatepickerMin?.min">
{{'USER.MACHINE.CHOOSEDATEAFTER' | translate}}:
{{dateControl?.errors?.matDatepickerMin.min.toDate() | localizedDate: 'EEE dd. MMM'}}
</span>
</cnsl-form-field>
</div>
<div mat-dialog-actions class="action">
<div mat-dialog-actions class=" action">
<button mat-button (click)="closeDialog()">
{{'ACTIONS.CANCEL' | translate}}
</button>
<button color="primary" mat-raised-button class="ok-button" [disabled]="type == undefined"
<button color="primary" mat-raised-button class="ok-button" [disabled]="type == undefined || dateControl.invalid"
(click)="closeDialogWithSuccess()">
{{'ACTIONS.ADD' | translate}}
</button>
@@ -1,6 +1,6 @@
import { Component, Inject } from '@angular/core';
import { FormControl } from '@angular/forms';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { Moment } from 'moment';
import { MachineKeyType } from 'src/app/proto/generated/management_pb';
@Component({
@@ -9,22 +9,26 @@ import { MachineKeyType } from 'src/app/proto/generated/management_pb';
styleUrls: ['./add-key-dialog.component.scss'],
})
export class AddKeyDialogComponent {
public startDate: Date = new Date();
types: MachineKeyType[] = [
MachineKeyType.MACHINEKEY_JSON,
];
date!: Moment;
public type: MachineKeyType = MachineKeyType.MACHINEKEY_JSON;
public dateControl: FormControl = new FormControl('', []);
constructor(
public dialogRef: MatDialogRef<AddKeyDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: any,
) { }
) {
const today = new Date();
this.startDate.setDate(today.getDate() + 1);
}
public closeDialog(): void {
this.dialogRef.close(false);
}
public closeDialogWithSuccess(): void {
this.dialogRef.close({ type: this.type, date: this.date });
this.dialogRef.close({ type: this.type, date: this.dateControl.value });
}
}
@@ -1,14 +1,14 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatMomentDateModule } from '@angular/material-moment-adapter';
import { MatButtonModule } from '@angular/material/button';
import { MatDatepickerModule } from '@angular/material/datepicker';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatSelectModule } from '@angular/material/select';
import { TranslateModule } from '@ngx-translate/core';
import { InputModule } from 'src/app/modules/input/input.module';
import { LocalizedDatePipeModule } from 'src/app/pipes/localized-date-pipe/localized-date-pipe.module';
import { AddKeyDialogComponent } from './add-key-dialog.component';
@@ -18,13 +18,14 @@ import { AddKeyDialogComponent } from './add-key-dialog.component';
CommonModule,
TranslateModule,
MatButtonModule,
MatFormFieldModule,
InputModule,
MatSelectModule,
MatInputModule,
MatIconModule,
FormsModule,
MatDatepickerModule,
MatMomentDateModule,
ReactiveFormsModule,
LocalizedDatePipeModule,
],
})
export class AddKeyDialogModule { }
@@ -87,18 +87,16 @@ export class MachineKeysComponent implements OnInit {
if (resp.date as Moment) {
const ts = new Timestamp();
console.log(resp.date.toDate().getTime());
console.log(resp.date.toDate());
const milliseconds = resp.date.toDate().getTime();
const seconds = Math.abs(milliseconds / 1000);
const nanos = (milliseconds - seconds * 1000) * 1000 * 1000;
ts.setSeconds(seconds);
ts.setNanos(nanos);
date = ts;
console.log(date.toObject());
}
if (type) {
console.log(this.userId, type, date);
return this.userService.AddMachineKey(this.userId, type, date).then((response) => {
if (response) {
setTimeout(() => {
@@ -126,7 +124,6 @@ export class MachineKeysComponent implements OnInit {
this.userService.SearchMachineKeys(this.userId, limit, offset).then(resp => {
this.keyResult = resp.toObject();
this.dataSource.data = this.keyResult.resultList;
console.log(this.keyResult.resultList);
this.loadingSubject.next(false);
}).catch((error: any) => {
this.toast.showError(error);
@@ -11,12 +11,14 @@
<p class="right">{{'USER.MACHINE.KEYTYPES.'+addedKey?.type | translate}}</p>
</div>
<div class="row">
<p class="left">{{'USER.MACHINE.EXPIRATIONDATE' | translate}}</p>
<p class="right">{{addedKey?.creationDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm' }}</p>
<p class="left">{{'USER.MACHINE.CREATIONDATE' | translate}}</p>
<p class="right">{{addedKey?.creationDate | timestampToDate | localizedDate: 'EEE dd. MMM YYYY, HH:mm' }}
</p>
</div>
<div class="row">
<p class="left">{{'USER.MACHINE.EXPIRATIONDATE' | translate}}</p>
<p class="right">{{addedKey?.expirationDate | timestampToDate | localizedDate: 'EEE dd. MMM, HH:mm'}}</p>
<p class="right">{{addedKey?.expirationDate | timestampToDate | localizedDate: 'EEE dd. MMM YYYY, HH:mm'}}
</p>
</div>
<button class="download-button" mat-stroked-button color="primary" (click)="saveFile()">Download</button>
</ng-container>
@@ -27,11 +27,6 @@
.action {
width: 40px;
}
.selection {
width: 50px;
max-width: 50px;
}
}
}
@@ -1,5 +1,5 @@
<div class="membership-groups">
<span class="header">{{ 'USER.MEMBERSHIPS.TITLE' | translate }}</span>
<span class="me-header">{{ 'USER.MEMBERSHIPS.TITLE' | translate }}</span>
<div class="people" *ngIf="memberships">
<div class="img-list" [@cardAnimation]="memberships.totalResult">
<mat-spinner class="spinner" diameter="20" *ngIf="loading"></mat-spinner>
@@ -34,9 +34,9 @@
<mat-icon>add</mat-icon>
</button>
<span class="fill-space"></span>
<button class="refresh-img" (click)="loadManager(user.id)" [disabled]="disabled" mat-icon-button
aria-label="refresh contributors">
<mat-icon>refresh</mat-icon>
<button matTooltip="{{'ACTIONS.REFRESH' | translate}}" class="refresh-img" (click)="loadManager(user.id)"
[disabled]="disabled" mat-icon-button aria-label="refresh contributors">
<mat-icon class="icon">refresh</mat-icon>
</button>
</div>
</div>
@@ -9,11 +9,14 @@
/* stylelint-enable */
.membership-groups {
.header {
.me-header {
display: block;
margin-bottom: 1rem;
font-weight: 400;
margin-top: 0;
font-size: 14px;
letter-spacing: .05em;
text-transform: uppercase;
}
.sub-header {
@@ -33,7 +36,6 @@
.img-list {
width: 100%;
margin-top: .5rem;
margin-left: 1rem;
display: flex;
align-items: center;
@@ -54,7 +56,11 @@
.refresh-img {
float: left;
margin: 0 8px 0 -15px;
margin: 0 0 0 -15px;
.icon {
font-size: 1.2rem;
}
}
.avatar-circle {
@@ -97,6 +103,7 @@
display: block;
white-space: nowrap;
overflow: hidden;
letter-spacing: .05em;
}
i {
@@ -10,22 +10,22 @@
<form *ngIf="passwordForm" autocomplete="new-password" [formGroup]="passwordForm"
(ngSubmit)="setInitialPassword(userId)">
<div class="content center">
<mat-form-field class="formfield" appearance="outline">
<mat-label>{{ 'USER.PASSWORD.NEW' | translate }}</mat-label>
<input autocomplete="off" name="password" matInput formControlName="password" type="password" />
<cnsl-form-field class="formfield" appearance="outline">
<cnsl-label>{{ 'USER.PASSWORD.NEW' | translate }}</cnsl-label>
<input cnslInput autocomplete="off" name="password" formControlName="password" type="password" />
<mat-error *ngIf="password?.errors?.required">
<span cnsl-error *ngIf="password?.errors?.required">
{{ 'USER.VALIDATION.REQUIRED' | translate }}
</mat-error>
</mat-form-field>
<mat-form-field class="formfield" appearance="outline">
<mat-label>{{ 'USER.PASSWORD.CONFIRM' | translate }}</mat-label>
<input autocomplete="off" name="passwordRepeat" matInput formControlName="confirmPassword"
</span>
</cnsl-form-field>
<cnsl-form-field class="formfield" appearance="outline">
<cnsl-label>{{ 'USER.PASSWORD.CONFIRM' | translate }}</cnsl-label>
<input cnslInput autocomplete="off" name="passwordRepeat" formControlName="confirmPassword"
type="password" />
<mat-error *ngIf="confirmPassword?.errors?.notequal">
<span cnsl-error *ngIf="confirmPassword?.errors?.notequal">
{{ 'USER.PASSWORD.NOTEQUAL' | translate }}
</mat-error>
</mat-form-field>
</span>
</cnsl-form-field>
</div>
<div class="btn-container">
<button class="submit-button" [disabled]="passwordForm.invalid" mat-raised-button
@@ -37,37 +37,37 @@
<ng-template #authUser>
<form *ngIf="passwordForm" [formGroup]="passwordForm" (ngSubmit)="setPassword()">
<div class="content">
<mat-form-field class="formfield" appearance="outline">
<mat-label>{{ 'USER.PASSWORD.OLD' | translate }}</mat-label>
<input autocomplete="off" name="password" type="password" matInput
<cnsl-form-field class="formfield" appearance="outline">
<cnsl-label>{{ 'USER.PASSWORD.OLD' | translate }}</cnsl-label>
<input cnslInput autocomplete="off" name="password" type="password"
formControlName="currentPassword" />
<mat-error *ngIf="currentPassword?.errors?.required">
<span cnsl-error *ngIf="currentPassword?.errors?.required">
{{ 'USER.PASSWORD.REQUIRED' | translate }}
</mat-error>
</mat-form-field>
</span>
</cnsl-form-field>
<div class="validation between" *ngIf="this.policy">
<app-password-complexity-view [policy]="this.policy" [password]="newPassword">
</app-password-complexity-view>
</div>
<mat-form-field class="formfield" appearance="outline">
<mat-label>{{ 'USER.PASSWORD.NEW' | translate }}</mat-label>
<input autocomplete="off" name="new password" type="password" matInput
<cnsl-form-field class="formfield" appearance="outline">
<cnsl-label>{{ 'USER.PASSWORD.NEW' | translate }}</cnsl-label>
<input cnslInput autocomplete="off" name="new password" type="password"
formControlName="newPassword" />
<mat-error *ngIf="newPassword?.errors?.required">
<span cnsl-error *ngIf="newPassword?.errors?.required">
{{ 'USER.VALIDATION.REQUIRED' | translate }}
</mat-error>
</mat-form-field>
<mat-form-field class="formfield" appearance="outline">
<mat-label>{{ 'USER.PASSWORD.CONFIRM' | translate }}</mat-label>
<input autocomplete="off" name="password repeating" type="password" matInput
</span>
</cnsl-form-field>
<cnsl-form-field class="formfield" appearance="outline">
<cnsl-label>{{ 'USER.PASSWORD.CONFIRM' | translate }}</cnsl-label>
<input cnslInput autocomplete="off" name="password repeating" type="password"
formControlName="confirmPassword" />
<mat-error *ngIf="confirmPassword?.errors?.notequal">
<span cnsl-error *ngIf="confirmPassword?.errors?.notequal">
{{ 'USER.PASSWORD.NOTEQUAL' | translate }}
</mat-error>
</mat-form-field>
</span>
</cnsl-form-field>
</div>
<button class="submit-button" [disabled]="passwordForm.invalid" mat-raised-button
color="primary">{{ 'USER.PASSWORD.RESET' | translate }}</button>
@@ -28,6 +28,6 @@
padding: .5rem 4rem;
}
.mat-error {
.cnsl-error {
display: block;
}
@@ -4,9 +4,7 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatDialogModule } from '@angular/material/dialog';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatPaginatorModule } from '@angular/material/paginator';
import { MatProgressBarModule } from '@angular/material/progress-bar';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
@@ -20,6 +18,7 @@ import { MemberCreateDialogModule } from 'src/app/modules/add-member-dialog/memb
import { CardModule } from 'src/app/modules/card/card.module';
import { ChangesModule } from 'src/app/modules/changes/changes.module';
import { DetailLayoutModule } from 'src/app/modules/detail-layout/detail-layout.module';
import { InputModule } from 'src/app/modules/input/input.module';
import { MetaLayoutModule } from 'src/app/modules/meta-layout/meta-layout.module';
import { PasswordComplexityViewModule } from 'src/app/modules/password-complexity-view/password-complexity-view.module';
import { RefreshTableModule } from 'src/app/modules/refresh-table/refresh-table.module';
@@ -83,9 +82,7 @@ import { UserMfaComponent } from './user-detail/user-mfa/user-mfa.component';
ShowKeyDialogModule,
MatCheckboxModule,
HasRolePipeModule,
MatFormFieldModule,
UserGrantsModule,
MatInputModule,
MatButtonModule,
MatIconModule,
CardModule,
@@ -104,6 +101,7 @@ import { UserMfaComponent } from './user-detail/user-mfa/user-mfa.component';
MemberCreateDialogModule,
TimestampToDatePipeModule,
LocalizedDatePipeModule,
InputModule,
],
})
export class UserDetailModule { }
@@ -89,7 +89,7 @@
<app-card *ngIf="user?.id" title="{{ 'GRANTS.USER.TITLE' | translate }}"
description="{{'GRANTS.USER.DESCRIPTION' | translate }}">
<app-user-grants [userId]="user.id" [context]="USERGRANTCONTEXT"
[displayedColumns]="['select', 'projectId', 'creationDate', 'changeDate', 'roleNamesList']"
[displayedColumns]="['select', 'projectId', 'dates', 'roleNamesList']"
[disableWrite]="((['user.grant.write$'] | hasRole) | async) == false"
[disableDelete]="((['user.grant.delete$'] | hasRole) | async) == false">
</app-user-grants>
@@ -102,12 +102,18 @@
<span class="first">{{'USER.PREFERRED_LOGINNAME' | translate}}</span>
<span class="second"><span style="display: block;">{{user.preferredLoginName}}</span></span>
</div>
<div class="row">
<span class="first">{{'ORG.PAGES.STATE' | translate}}</span>
<span *ngIf="user && user.state !== undefined" class="state"
[ngClass]="{'active': user.state === UserState.USERSTATE_ACTIVE, 'inactive': user.state === UserState.USERSTATE_INACTIVE}">{{'USER.DATA.STATE'+user.state | translate}}</span>
</div>
</div>
<ng-template appHasRole [appHasRole]="['user.membership.read']">
<app-memberships [user]="user" [disabled]="(canWrite$ | async) == false"></app-memberships>
</ng-template>
<app-changes class="changes" [changeType]="ChangeType.USER" [id]="user.id"></app-changes>
<app-changes class="changes" [refresh]="refreshChanges$" [changeType]="ChangeType.USER" [id]="user.id">
</app-changes>
</div>
</app-meta-layout>
@@ -41,18 +41,36 @@
.first {
flex: 1;
font-size: .8rem;
font-size: 13px;
margin-right: .5rem;
white-space: nowrap;
}
.second {
font-size: .8rem;
font-size: .13px;
text-overflow: ellipsis;
overflow: hidden;
margin-left: 1rem;
text-align: right;
}
.state {
border-radius: 50vw;
padding: 2px .5rem;
letter-spacing: .05em;
font-size: 11px;
background-color: #8795a120;
&.active {
background-color: #85d996;
color: black;
}
&.inactive {
background-color: #ff8981;
color: black;
}
}
}
}
@@ -1,7 +1,7 @@
import { Location } from '@angular/common';
import { Component, OnInit } from '@angular/core';
import { Component, EventEmitter, OnInit } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { ActivatedRoute } from '@angular/router';
import { ActivatedRoute, Params, Router } from '@angular/router';
import { TranslateService } from '@ngx-translate/core';
import { take } from 'rxjs/operators';
import { ChangeType } from 'src/app/modules/changes/changes.component';
@@ -47,6 +47,7 @@ export class UserDetailComponent implements OnInit {
public USERGRANTCONTEXT: UserGrantContext = UserGrantContext.USER;
public EditDialogType: any = EditDialogType;
public refreshChanges$: EventEmitter<void> = new EventEmitter();
constructor(
public translate: TranslateService,
@@ -55,9 +56,11 @@ export class UserDetailComponent implements OnInit {
public mgmtUserService: ManagementService,
private _location: Location,
private dialog: MatDialog,
private router: Router,
) { }
refreshUser(): void {
this.refreshChanges$.emit();
this.route.params.pipe(take(1)).subscribe(params => {
const { id } = params;
this.mgmtUserService.GetUserByID(id).then(user => {
@@ -109,6 +112,7 @@ export class UserDetailComponent implements OnInit {
.then((data: UserProfile) => {
this.toast.showInfo('USER.TOAST.SAVED', true);
this.user = Object.assign(this.user, data.toObject());
this.refreshChanges$.emit();
})
.catch(error => {
this.toast.showError(error);
@@ -128,6 +132,7 @@ export class UserDetailComponent implements OnInit {
.then((data: MachineResponse) => {
this.toast.showInfo('USER.TOAST.SAVED', true);
this.user = Object.assign(this.user, data.toObject());
this.refreshChanges$.emit();
})
.catch(error => {
this.toast.showError(error);
@@ -138,15 +143,16 @@ export class UserDetailComponent implements OnInit {
public resendEmailVerification(): void {
this.mgmtUserService.ResendEmailVerification(this.user.id).then(() => {
this.toast.showInfo('USER.TOAST.EMAILVERIFICATIONSENT', true);
this.refreshChanges$.emit();
}).catch(error => {
this.toast.showError(error);
});
}
public resendPhoneVerification(): void {
console.log('resend phone ver', this.user.id);
this.mgmtUserService.ResendPhoneVerification(this.user.id).then(() => {
this.toast.showInfo('USER.TOAST.PHONEVERIFICATIONSENT', true);
this.refreshChanges$.emit();
}).catch(error => {
this.toast.showError(error);
});
@@ -201,6 +207,7 @@ export class UserDetailComponent implements OnInit {
this.mgmtUserService.SendSetPasswordNotification(this.user.id, NotificationType.NOTIFICATIONTYPE_EMAIL)
.then(() => {
this.toast.showInfo('USER.TOAST.PASSWORDNOTIFICATIONSENT', true);
this.refreshChanges$.emit();
}).catch(error => {
this.toast.showError(error);
});
@@ -220,7 +227,10 @@ export class UserDetailComponent implements OnInit {
dialogRef.afterClosed().subscribe(resp => {
if (resp) {
this.mgmtUserService.DeleteUser(this.user.id).then(() => {
this.navigateBack();
const params: Params = {
'deferredReload': true,
};
this.router.navigate(['/users/list', this.user.human ? 'humans' : 'machines'], { queryParams: params });
this.toast.showInfo('USER.TOAST.DELETED', true);
}).catch(error => {
this.toast.showError(error);
@@ -238,6 +248,7 @@ export class UserDetailComponent implements OnInit {
if (resp.send && this.user.id) {
this.mgmtUserService.ResendInitialMail(this.user.id, resp.email ?? '').then(() => {
this.toast.showInfo('USER.TOAST.INITEMAILSENT', true);
this.refreshChanges$.emit();
}).catch(error => {
this.toast.showError(error);
});
@@ -4,9 +4,7 @@ import { FormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatDialogModule } from '@angular/material/dialog';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatPaginatorModule } from '@angular/material/paginator';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatTableModule } from '@angular/material/table';
@@ -15,6 +13,7 @@ import { TranslateModule } from '@ngx-translate/core';
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
import { AvatarModule } from 'src/app/modules/avatar/avatar.module';
import { CardModule } from 'src/app/modules/card/card.module';
import { InputModule } from 'src/app/modules/input/input.module';
import { RefreshTableModule } from 'src/app/modules/refresh-table/refresh-table.module';
import { SharedModule } from 'src/app/modules/shared/shared.module';
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module';
@@ -48,8 +47,7 @@ import { UserTableComponent } from './user-table/user-table.component';
TranslateModule,
SharedModule,
RefreshTableModule,
MatFormFieldModule,
MatInputModule,
InputModule,
],
exports: [
UserListComponent,
@@ -1,11 +1,10 @@
<app-refresh-table [loading]="loading$ | async" (refreshed)="refreshPage()" [dataSize]="dataSource.data.length"
[timestamp]="userResult?.viewTimestamp" [selection]="selection"
[emitRefreshOnPreviousRoutes]="refreshOnPreviousRoutes">
<mat-form-field @appearfade *ngIf="userSearchKey != undefined" actions class="filtername">
<mat-label>{{'USER.PAGES.FILTER' | translate}}</mat-label>
<input matInput (keyup)="applyFilter($event)"
<cnsl-form-field @appearfade *ngIf="userSearchKey != undefined" actions class="filtername">
<input cnslInput (keyup)="applyFilter($event)"
[placeholder]="('USER.TABLE.FILTER.' + userSearchKey.toString()) | translate" #input>
</mat-form-field>
</cnsl-form-field>
<ng-template appHasRole [appHasRole]="['user.write']" actions>
<button (click)="deactivateSelectedUsers()" matTooltip="{{'USER.TABLE.DEACTIVATE' | translate}}"
@@ -119,7 +118,9 @@
<ng-container matColumnDef="state">
<th mat-header-cell *matHeaderCellDef> {{ 'USER.DATA.STATE' | translate }} </th>
<td mat-cell *matCellDef="let user" [routerLink]="user.id ? ['/users', user.id ]: null">
{{ 'USER.DATA.STATE'+user.state | translate }} </td>
<span class="state"
[ngClass]="{'active': user.state === UserState.USERSTATE_ACTIVE, 'inactive': user.state === UserState.USERSTATE_INACTIVE}">{{ 'USER.DATA.STATE'+user.state | translate }}</span>
</td>
</ng-container>
<ng-container matColumnDef="actions" stickyEnd>
@@ -137,6 +138,10 @@
<tr class="highlight" mat-row *matRowDef="let row; columns: displayedColumns;"> </tr>
</table>
<div *ngIf="(loading$ | async) == false && !dataSource?.data?.length" class="no-content-row">
<i class="las la-exclamation"></i>
<span>{{'USER.TABLE.EMPTY' | translate}}</span>
</div>
<mat-paginator #paginator class="paginator" [length]="userResult?.totalResult || 0" [pageSize]="10"
[pageSizeOptions]="[5, 10, 20]" (page)="changePage($event)"></mat-paginator>
</div>
@@ -144,7 +149,7 @@
<ng-template #templateRef let-key="key">
<button class="search-button" mat-icon-button (click)="setFilter(key)">
<mat-icon *ngIf="this.userSearchKey != key">search</mat-icon>
<mat-icon *ngIf="this.userSearchKey == key">search_off</mat-icon>
<mat-icon class="icon" *ngIf="this.userSearchKey != key">search</mat-icon>
<mat-icon class="icon" *ngIf="this.userSearchKey == key">search_off</mat-icon>
</button>
</ng-template>
@@ -7,21 +7,27 @@
td,
th {
padding: .5rem;
outline: none;
padding-left: .5rem;
padding-right: .5rem;
&:first-child {
padding-left: 0;
padding-right: 1rem;
padding-right: .5rem;
}
&:last-child {
padding-right: 0;
}
}
.search-button,
.dlt-button {
th {
.search-button {
visibility: hidden;
width: 30px;
.icon {
font-size: 1.2rem;
}
}
&:hover,
@@ -33,17 +39,16 @@
}
tr {
.dlt-button {
visibility: hidden;
}
&:hover {
.dlt-button {
visibility: visible;
}
}
}
.selection {
width: 50px;
max-width: 50px;
}
}
}
@@ -60,3 +65,25 @@
margin: auto;
}
}
td {
outline: none;
}
.state {
border-radius: 50vw;
padding: 2px .5rem;
letter-spacing: .05em;
font-size: 11px;
background-color: #8795a120;
&.active {
background-color: #85d996;
color: black;
}
&.inactive {
background-color: #ff8981;
color: black;
}
}
@@ -1,15 +1,22 @@
import { SelectionModel } from '@angular/cdk/collections';
import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { MatInput } from '@angular/material/input';
import { MatPaginator, PageEvent } from '@angular/material/paginator';
import { MatTableDataSource } from '@angular/material/table';
import { ActivatedRoute } from '@angular/router';
import { TranslateService } from '@ngx-translate/core';
import { BehaviorSubject, Observable } from 'rxjs';
import { take } from 'rxjs/operators';
import { enterAnimations } from 'src/app/animations';
import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component';
import { UserView } from 'src/app/proto/generated/auth_pb';
import { SearchMethod, UserSearchKey, UserSearchQuery, UserSearchResponse } from 'src/app/proto/generated/management_pb';
import {
SearchMethod,
UserSearchKey,
UserSearchQuery,
UserSearchResponse,
UserState,
} from 'src/app/proto/generated/management_pb';
import { ManagementService } from 'src/app/services/mgmt.service';
import { ToastService } from 'src/app/services/toast.service';
@@ -30,7 +37,7 @@ export class UserTableComponent implements OnInit {
@Input() refreshOnPreviousRoutes: string[] = [];
@Input() disabled: boolean = false;
@ViewChild(MatPaginator) public paginator!: MatPaginator;
@ViewChild('input') public filter!: MatInput;
@ViewChild('input') public filter!: Input;
public dataSource: MatTableDataSource<UserView.AsObject> = new MatTableDataSource<UserView.AsObject>();
public selection: SelectionModel<UserView.AsObject> = new SelectionModel<UserView.AsObject>(true, []);
public userResult!: UserSearchResponse.AsObject;
@@ -41,11 +48,14 @@ export class UserTableComponent implements OnInit {
@Output() public changedSelection: EventEmitter<Array<UserView.AsObject>> = new EventEmitter();
UserSearchKey: any = UserSearchKey;
public UserState: any = UserState;
constructor(
public translate: TranslateService,
private userService: ManagementService,
private toast: ToastService,
private dialog: MatDialog,
private route: ActivatedRoute,
) {
this.selection.changed.subscribe(() => {
this.changedSelection.emit(this.selection.selected);
@@ -53,7 +63,14 @@ export class UserTableComponent implements OnInit {
}
ngOnInit(): void {
this.getData(10, 0, this.userType);
this.route.queryParams.pipe(take(1)).subscribe(params => {
this.getData(10, 0, this.userType);
if (params.deferredReload) {
setTimeout(() => {
this.getData(10, 0, this.userType);
}, 2000);
}
});
}
public isAllSelected(): boolean {
@@ -78,7 +95,12 @@ export class UserTableComponent implements OnInit {
return this.userService.DeactivateUser(value.id);
})).then(() => {
this.toast.showInfo('USER.TOAST.SELECTEDDEACTIVATED', true);
this.getData(10, 0, this.userType);
this.selection.clear();
setTimeout(() => {
this.refreshPage();
}, 1000);
}).catch(error => {
this.toast.showError(error);
});
}
@@ -87,7 +109,12 @@ export class UserTableComponent implements OnInit {
return this.userService.ReactivateUser(value.id);
})).then(() => {
this.toast.showInfo('USER.TOAST.SELECTEDREACTIVATED', true);
this.getData(10, 0, this.userType);
this.selection.clear();
setTimeout(() => {
this.refreshPage();
}, 1000);
}).catch(error => {
this.toast.showError(error);
});
}