feat(console): add stylelint scss, app redirecturi validation, user grant api change (#531)

* add custom validator for redirect inputs

* change http pattern, i18n

* throw out deprecated usergrant functions

* remove deprecation service

* show docs in app detail

* reorder data promises

* show redirect desc in app detail

* custom validator

* grant search filters

* rm animations

* add validation to app detail

* user grant udpate

* rm grantid from update req

* fix project member guard

* fix hasrole directive

* show validation errors, i18n

* fix router link from org members

* navitem padding

* mobile layout

* policy grid mobile layout

* rm unused background

* lint

* add stylelinter add to pipeline

* update stylelint rules

* lint unknown rule

* disable enable rules

* lint

* table style lint

* table classes

* fix stylelinter rule, lint

* overflow fix member detail

* common detail page

* user create, password, project grant detail clnup

* move meta styles

* lint

* password policy indicator

* lint

* fix org create

* common component for complexity view

* lint

* user grant filter

* 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/en.json

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

Co-authored-by: Florian Forster <[email protected]>
This commit is contained in:
Max Peintner
2020-07-30 16:54:15 +02:00
committed by GitHub
co-authored by Florian Forster
parent 8d1725a81d
commit 22ba33c2ef
154 changed files with 6778 additions and 6156 deletions
@@ -1,103 +1,87 @@
<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>
<app-detail-layout [backRouterLink]="[ '/users/all']" title="{{ 'USER.CREATE.TITLE' | translate }}"
description="{{ 'USER.CREATE.DESCRIPTION' | translate }}">
<mat-progress-bar *ngIf="loading" color="accent" mode="indeterminate"></mat-progress-bar>
<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">
<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>
</div>
<div class="right">
<div class="head">
<h1>{{ 'USER.CREATE.TITLE' | translate }}</h1>
<p class="desc">{{ 'USER.CREATE.DESCRIPTION' | translate }}</p>
</div>
<mat-progress-bar *ngIf="loading" color="accent" mode="indeterminate"></mat-progress-bar>
<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>
</div>
<div class="btn-container">
<button color="primary" [disabled]="userForm.invalid" type="submit"
mat-raised-button>{{ 'ACTIONS.CREATE' | translate }}</button>
</div>
</form>
<div class="btn-container">
<button color="primary" [disabled]="userForm.invalid" type="submit"
mat-raised-button>{{ 'ACTIONS.CREATE' | translate }}</button>
</div>
</div>
</div>
</form>
</app-detail-layout>
@@ -1,82 +1,33 @@
.container {
display: flex;
padding-bottom: 3rem;
.form {
width: 100%;
padding-top: 1rem;
.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;
}
}
.btn-container {
button {
margin-top: 3rem;
display: block;
padding: .5rem 4rem;
border-radius: .5rem;
}
}
}
.content {
width: 100%;
display: flex wrap;
flex-direction: row;
margin: 0 -.5rem;
.section {
padding: .5rem;
flex-basis: 100%;
color: #8795a1;
font-size: .9rem;
}
.formfield {
flex: 1 0 33%;
margin: 0 .5rem;
}
}
@@ -11,6 +11,7 @@ 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 { UserCreateRoutingModule } from './user-create-routing.module';
import { UserCreateComponent } from './user-create.component';
@@ -34,6 +35,7 @@ import { UserCreateComponent } from './user-create.component';
MatCheckboxModule,
MatTooltipModule,
TranslateModule,
DetailLayoutModule,
],
})
export class UserCreateModule { }
@@ -1,6 +1,6 @@
<app-meta-layout>
<div class="max-width-container">
<h1>{{ 'USER.TITLE' | translate }}</h1>
<h1 class="h1">{{ 'USER.TITLE' | translate }}</h1>
<p class="sub">{{'USER.DESCRIPTION' | translate}}</p>
<mat-progress-bar *ngIf="loading" color="accent" mode="indeterminate"></mat-progress-bar>
@@ -21,13 +21,13 @@
</app-card>
<div class="col" *ngIf="user">
<app-card title="{{ 'USER.PROFILE.TITLE' | translate }}">
<app-card class="app-card" title="{{ 'USER.PROFILE.TITLE' | translate }}">
<app-detail-form [genders]="genders" [languages]="languages" [profile]="user"
(changedLanguage)="changedLanguage($event)" (submitData)="saveProfile($event)">
</app-detail-form>
</app-card>
<app-card title="Theme" class="theme-card">
<app-card title="Theme" class="app-card theme-card">
<app-theme-setting></app-theme-setting>
</app-card>
</div>
@@ -41,7 +41,7 @@
<span>*********</span>
<div class="actions">
<a [routerLink]="['password']" mat-icon-button>
<mat-icon>chevron_right</mat-icon>
<mat-icon class="icon">chevron_right</mat-icon>
</a>
</div>
</div>
@@ -52,11 +52,12 @@
<ng-container *ngIf="!emailEditState; else emailEdit">
<div class="actions">
<span class="name">{{user?.email}}</span>
<mat-icon *ngIf="user?.isEmailVerified" color="primary" aria-hidden="false"
<mat-icon class="icon" *ngIf="user?.isEmailVerified" color="primary" aria-hidden="false"
aria-label="verified icon">
check_circle_outline</mat-icon>
<ng-container *ngIf="user?.email && !user?.isEmailVerified">
<mat-icon color="warn" aria-hidden="false" aria-label="not verified icon">highlight_off
<mat-icon class="icon" color="warn" aria-hidden="false" aria-label="not verified icon">
highlight_off
</mat-icon>
<a class="verify" matTooltip="{{'USER.LOGINMETHODS.EMAIL.RESEND' | translate}}"
(click)="resendVerification()">{{'USER.LOGINMETHODS.RESENDCODE' | translate}}</a>
@@ -65,7 +66,7 @@
<div class="actions">
<button (click)="emailEditState = true" mat-icon-button>
<mat-icon>edit</mat-icon>
<mat-icon class="icon">edit</mat-icon>
</button>
</div>
</ng-container>
@@ -75,7 +76,7 @@
<input matInput [(ngModel)]="user.email" />
</mat-form-field>
<button (click)="emailEditState = false" mat-icon-button>
<mat-icon>close</mat-icon>
<mat-icon class="icon">close</mat-icon>
</button>
<button [disabled]="!user.email" class="submit-button" type="button" color="primary"
(click)="saveEmail()" mat-raised-button>{{ 'ACTIONS.SAVE' | translate }}</button>
@@ -88,11 +89,12 @@
<ng-container *ngIf="!phoneEditState; else phoneEdit">
<div class="actions">
<span class="name">{{user?.phone}}</span>
<mat-icon *ngIf="user?.isPhoneVerified" color="primary" aria-hidden="false"
<mat-icon class="icon" *ngIf="user?.isPhoneVerified" color="primary" aria-hidden="false"
aria-label="verified icon">
check_circle_outline</mat-icon>
<ng-container *ngIf="user?.phone && !user?.isPhoneVerified">
<mat-icon color="warn" aria-hidden="false" aria-label="not verified icon">highlight_off
<mat-icon class="icon" color="warn" aria-hidden="false" aria-label="not verified icon">
highlight_off
</mat-icon>
<a class="verify" matTooltip="{{'USER.LOGINMETHODS.ENTERCODE_DESC' | translate}}"
(click)="enterCode()">{{'USER.LOGINMETHODS.ENTERCODE' | translate}}</a>
@@ -103,7 +105,7 @@
<div class="actions">
<button (click)="phoneEditState = true" mat-icon-button>
<mat-icon>edit</mat-icon>
<mat-icon class="icon">edit</mat-icon>
</button>
</div>
</ng-container>
@@ -114,7 +116,7 @@
<input matInput [(ngModel)]="user.phone" />
</mat-form-field>
<button (click)="phoneEditState = false" mat-icon-button>
<mat-icon>close</mat-icon>
<mat-icon class="icon">close</mat-icon>
</button>
<button *ngIf="user.phone" color="warn" (click)="deletePhone()" mat-icon-button>
<i class="las la-trash"></i>
@@ -137,6 +139,6 @@
</div>
</div>
<app-changes [changeType]="ChangeType.MYUSER" [id]="user.id"></app-changes>
<app-changes class="changes" [changeType]="ChangeType.MYUSER" [id]="user.id"></app-changes>
</div>
</app-meta-layout>
@@ -1,286 +1,154 @@
h1 {
.h1 {
margin-top: 0;
}
.avatar-section {
.sub {
color: #8795a1;
margin-bottom: 2rem;
}
.login-name-row {
display: flex;
align-items: center;
span {
color: #8795a1;
text-transform: uppercase;
margin-right: 3rem;
font-size: .9rem;
button {
transition: opacity .15s ease-in-out;
visibility: hidden;
opacity: 0;
&[disabled] {
visibility: visible;
color: white;
opacity: 1;
}
}
img {
height: 80px;
width: 80px;
background-size: cover;
background-position: center;
background-repeat: no-repeat;
border-radius: 50%;
margin: .5rem;
border: 2px solid #8795a1;
&:hover {
button {
visibility: visible;
opacity: 1;
}
}
}
.sub {
color: #8795a1;
margin-bottom: 2rem;
}
.login-name-row {
display: flex;
align-items: center;
button {
transition: opacity .15s ease-in-out;
visibility: hidden;
opacity: 0;
&[disabled] {
visibility: visible;
color: white;
opacity: 1;
}
}
&:hover {
button {
visibility: visible;
opacity: 1;
}
}
}
.content {
display: flex;
flex-direction: row;
flex-wrap: wrap;
margin: 0 -.5rem;
&.nowrap{
flex-wrap: nowrap;
}
mat-form-field {
flex: 1;
margin: 0 .5rem;
}
}
.btn-container {
display: flex;
justify-content: flex-end;
margin: 0 -.5rem;
button {
border-radius: .5rem;
margin: 0 .5rem;
}
}
.method-col {
display: flex;
flex-direction: column;
margin: -.5rem;
.method-row {
display: flex;
justify-content: flex-end;
margin: 0 -.5rem;
button {
align-items: center;
justify-content: space-evenly;
padding: .5rem;
border-bottom: 1px solid #ffffff20;
flex-wrap: wrap;
.label,
.name {
margin-right: 1rem;
}
.actions {
flex: 1;
display: flex;
justify-content: flex-end;
align-items: center;
}
.label {
flex: 1;
font-size: .9rem;
color: #8795a1;
}
.icon {
margin-right: .5rem;
}
.submit-button {
border-radius: .5rem;
margin: 0 .5rem;
}
.verify {
text-decoration: none;
font-size: .8rem;
color: #8795a1;
border-radius: .5rem;
cursor: pointer;
word-wrap: none;
white-space: nowrap;
margin-right: 1rem;
&:hover {
color: white;
text-decoration: underline;
}
}
}
.method-col {
display: flex;
flex-direction: column;
margin: -.5rem;
.method-row {
display: flex;
align-items: center;
justify-content: space-evenly;
padding: .5rem;
border-bottom: 1px solid #ffffff20;
flex-wrap: wrap;
.label, .name {
margin-right: 1rem;
}
.actions {
flex: 1;
display: flex;
justify-content: flex-end;
align-items: center;
}
.label {
flex: 1;
font-size: .9rem;
color: #8795a1;
}
mat-icon {
margin-right: .5rem;
}
.submit-button {
border-radius: .5rem;
}
.verify {
text-decoration: none;
font-size: .8rem;
color: #8795a1;
border-radius: .5rem;
cursor: pointer;
word-wrap: none;
white-space: nowrap;
margin-right: 1rem;
&:hover {
color: white;
text-decoration: underline;
}
}
}
}
.col {
display: flex;
flex-wrap: wrap;
align-items: stretch;
margin: -.5rem;
display: flex;
flex-wrap: wrap;
align-items: stretch;
margin: -1.5rem -.5rem;
app-card {
flex: 1;
margin: .5rem
}
.app-card {
flex: 1;
margin: .5rem;
}
.theme-card {
max-width: 300px;
}
.theme-card {
max-width: 300px;
.pwd-row {
display: flex;
align-items: center;
span {
flex: 1;
}
.title {
color: #8795a1;
font-size: 0.9rem;
}
@media only screen and (max-width: 450px) {
max-width: none;
}
}
}
.side {
.details {
margin-bottom: 1rem;
border-bottom: 1px solid #81868a40;
padding-bottom: 1rem;
.row {
display: flex;
margin-bottom: 0.5rem;
align-items: center;
button {
display: none;
visibility: hidden;
}
&:hover {
button {
display: inline-block;
visibility: visible;
mat-icon {
font-size: 1.2rem;
}
}
}
.first {
flex: 1;
font-size: 0.8rem;
margin-right: 0.5rem;
white-space: nowrap;
}
.fill-space {
flex: 1;
}
.second {
font-size: 0.8rem;
text-overflow: ellipsis;
overflow: hidden;
margin-left: 1rem;
text-align: right;
}
.details {
margin-bottom: 1rem;
border-bottom: 1px solid #81868a40;
padding-bottom: 1rem;
a {
&:hover {
cursor: pointer;
text-decoration: underline;
}
}
}
.row {
display: flex;
margin-bottom: .5rem;
align-items: center;
.side-section {
color: #8795a1;
}
}
.details {
margin-bottom: 1rem;
border-bottom: 1px solid #81868a40;
padding-bottom: 1rem;
.row {
display: flex;
margin-bottom: 0.5rem;
align-items: center;
button {
display: none;
visibility: hidden;
}
&:hover {
button {
display: inline-block;
visibility: visible;
mat-icon {
font-size: 1.2rem;
}
}
}
.first {
flex: 1;
font-size: 0.8rem;
margin-right: 0.5rem;
}
.fill-space {
flex: 1;
}
.second {
font-size: 0.8rem;
text-overflow: ellipsis;
overflow: hidden;
margin-left: 1rem;
}
a {
&:hover {
cursor: pointer;
text-decoration: underline;
}
}
}
.side-section {
color: #8795a1;
}
}
app-changes {
.first {
flex: 1;
min-height: 0;
font-size: .8rem;
margin-right: .5rem;
white-space: nowrap;
}
.second {
font-size: .8rem;
text-overflow: ellipsis;
overflow: hidden;
margin-left: 1rem;
text-align: right;
}
}
.changes {
flex: 1;
min-height: 0;
}
}
}
@@ -1,6 +1,6 @@
<app-card title="{{'USER.MFA.TITLE' | translate}}" description="{{'USER.MFA.DESCRIPTION' | translate}}">
<app-refresh-table [loading]="loading$ | async" (refreshed)="getOTP()" [dataSize]="dataSource?.data?.length">
<table class="background-style" mat-table [dataSource]="dataSource">
<table class="table background-style" mat-table [dataSource]="dataSource">
<ng-container matColumnDef="type">
<th mat-header-cell *matHeaderCellDef> {{ 'USER.MFA.TABLETYPE' | translate }} </th>
<td mat-cell *matCellDef="let mfa"> {{'USER.MFA.TYPE.'+ mfa.type | translate}} </td>
@@ -33,9 +33,9 @@
</table>
</app-refresh-table>
<div class="add-row">
<button *ngIf="otpAvailable" (click)="addOTP()" mat-stroked-button color="primary"
<button class="button" *ngIf="otpAvailable" (click)="addOTP()" mat-stroked-button color="primary"
matTooltip="{{'ACTIONS.NEW' | translate}}">
<mat-icon svgIcon="mdi_radar"></mat-icon>{{'USER.MFA.OTP' | translate}}
<mat-icon class="icon" svgIcon="mdi_radar"></mat-icon>{{'USER.MFA.OTP' | translate}}
</button>
</div>
<div class="table-wrapper">
@@ -1,40 +1,41 @@
.add-row {
display: flex;
margin: -.5rem;
.add-row {
display: flex;
margin: -.5rem;
button {
border-radius: .5rem;
margin: .5rem;
margin-top: 1rem;
.button {
border-radius: .5rem;
margin: .5rem;
margin-top: 1rem;
mat-icon {
margin-right: .5rem;
}
}
}
.centered {
display: flex;
align-items: center;
i {
margin-left: 1rem;
color: #5282c1;
}
}
table {
width: 100%;
td, th {
&:first-child {
padding-left: 0;
padding-right: 1rem;
}
&:last-child {
padding-right: 0;
}
.icon {
margin-right: .5rem;
}
}
}
}
.centered {
display: flex;
align-items: center;
i {
margin-left: 1rem;
color: #5282c1;
}
}
.table {
width: 100%;
td,
th {
&:first-child {
padding-left: 0;
padding-right: 1rem;
}
&:last-child {
padding-right: 0;
}
}
}
@@ -1,16 +1,16 @@
.formfield {
width: 100%;
width: 100%;
}
.action {
display: flex;
justify-content: flex-end;
.ok-button {
margin-left: 0.5rem;
}
button {
border-radius: 0.5rem;
}
display: flex;
justify-content: flex-end;
.ok-button {
margin-left: .5rem;
}
button {
border-radius: .5rem;
}
}
@@ -5,7 +5,7 @@
<qrcode *ngIf="data" class="qrcode" [qrdata]="data" [width]="150" [errorCorrectionLevel]="'M'"></qrcode>
</div>
<mat-form-field label="Access Code" required="true">
<mat-form-field class="form-field" label="Access Code" required="true">
<mat-label>Code</mat-label>
<input matInput [(ngModel)]="code" />
</mat-form-field>
@@ -7,8 +7,8 @@
}
}
mat-form-field {
width: 100%;
.form-field {
width: 100%;
}
.action {
@@ -1,11 +1,14 @@
@import '~@angular/material/theming';
@mixin theme-card($theme) {
$primary: map-get($theme, primary);
$primary-dark: mat-color($primary, A800);
/* stylelint-disable */
$primary: map-get($theme, primary);
$primary-dark: mat-color($primary, A800);
/* stylelint-enable */
.theme-conent, .crescent {
background-color: $primary-dark;
transition: background-color .4s cubic-bezier(0.645, 0.045, 0.355, 1);
}
.theme-conent,
.crescent {
background-color: $primary-dark;
transition: background-color .4s cubic-bezier(.645, .045, .355, 1);
}
}
@@ -2,11 +2,11 @@ $dark-background: #2d2e30;
$light-background: #fafafa;
:root {
transition: none;
transition: none;
}
.theme-app {
position: relative;
position: relative;
}
.theme-content {
@@ -20,9 +20,9 @@ $light-background: #fafafa;
border-radius: 100%;
width: 6rem;
height: 6rem;
background: linear-gradient(40deg, #FF0080,#FF8C00 70%);
background: linear-gradient(40deg, #ff0080, #ff8c00 70%);
margin: auto;
box-shadow: 0 30px 60px rgba(0,0,0,0.12);
box-shadow: 0 30px 60px rgba(0, 0, 0, .12);
}
.crescent {
@@ -34,10 +34,10 @@ $light-background: #fafafa;
background: $light-background;
transform: scale(0);
transform-origin: top right;
transition: transform .4s cubic-bezier(0.645, 0.045, 0.355, 1);
transition: transform .4s cubic-bezier(.645, .045, .355, 1);
}
p {
p {
font-size: 90%;
}
@@ -48,28 +48,29 @@ p {
}
.desc {
font-size: 14px;
color: #8795a1;
font-size: 14px;
color: #8795a1;
}
label, .toggle {
label,
.toggle {
height: 2.8rem;
border-radius: 100px;
}
label {
width: 100%;
background-color: rgba(0,0,0,.1);
background-color: rgba(0, 0, 0, .1);
border-radius: 100px;
position: relative;
margin: 1rem 0 1rem 0;
margin: 1rem 0 1rem 0;
cursor: pointer;
}
.toggle {
position: absolute;
background-color: $light-background;
box-shadow: 0 2px 15px rgba(0,0,0,.15);
box-shadow: 0 2px 15px rgba(0, 0, 0, .15);
}
.names {
@@ -88,33 +89,33 @@ label {
display: none;
}
[type="checkbox"]:checked + .theme-app .toggle{
[type="checkbox"]:checked + .theme-app .toggle {
transform: translateX(100%);
background-color: $dark-background;
}
[type="checkbox"]:checked + .theme-app .dark{
[type="checkbox"]:checked + .theme-app .dark {
opacity: 1;
}
[type="checkbox"]:checked + .theme-app .light{
[type="checkbox"]:checked + .theme-app .light {
opacity: .5;
}
[type="checkbox"]:checked + .theme-app{
[type="checkbox"]:checked + .theme-app {
background-color: inherit;
color: inherit;
}
[type="checkbox"]:checked + .theme-app .crescent{
[type="checkbox"]:checked + .theme-app .crescent {
transform: scale(1);
background: $dark-background;
}
[type="checkbox"]:checked + .theme-app .circle{
background: linear-gradient(40deg, #8983F7, #A3DAFB 70%);
[type="checkbox"]:checked + .theme-app .circle {
background: linear-gradient(40deg, #8983f7, #a3dafb 70%);
}
[type="checkbox"]:checked + .theme-app .main-circle{
background: linear-gradient(40deg, #8983F7, #A3DAFB 70%);
}
[type="checkbox"]:checked + .theme-app .main-circle {
background: linear-gradient(40deg, #8983f7, #a3dafb 70%);
}
@@ -34,6 +34,7 @@
</mat-form-field>
</div>
<div class="btn-container">
<button type="submit" color="primary" mat-raised-button>{{ 'ACTIONS.SAVE' | translate }}</button>
<button class="submit-button" type="submit" color="primary"
mat-raised-button>{{ 'ACTIONS.SAVE' | translate }}</button>
</div>
</form>
@@ -1,25 +1,21 @@
.content {
display: flex;
flex-direction: row;
flex-wrap: wrap;
margin: 0 -.5rem;
&.nowrap{
flex-wrap: nowrap;
}
mat-form-field {
flex: 1 1 33%;
margin: 0 .5rem;
}
display: flex;
flex-direction: row;
flex-wrap: wrap;
margin: 0 -.5rem;
.formfield {
flex: 1 1 33%;
margin: 0 .5rem;
}
}
.btn-container {
display: flex;
justify-content: flex-end;
button {
border-radius: .5rem;
}
}
display: flex;
justify-content: flex-end;
.submit-button {
border-radius: .5rem;
}
}
@@ -1,151 +1,76 @@
<div class="max-width-container">
<div class="container">
<div class="left">
<a *ngIf="userId" [routerLink]="[ '/user',userId]" mat-icon-button>
<mat-icon class="icon">arrow_back</mat-icon>
</a>
<a *ngIf="!userId" [routerLink]="[ '/users/me']" mat-icon-button>
<mat-icon class="icon">arrow_back</mat-icon>
</a>
<app-detail-layout [backRouterLink]="userId ? [ '/users',userId] : [ '/users/me']"
title="{{ 'USER.PASSWORD.TITLE' | translate }}" description="{{ 'USER.PASSWORD.DESCRIPTION' | translate }}">
<ng-container *ngIf="userId; else authUser">
<div class="validation" *ngIf="this.policy">
<app-password-complexity-view [policy]="this.policy" [password]="password">
</app-password-complexity-view>
</div>
<div class="right">
<div class="head">
<h1>{{ 'USER.PASSWORD.TITLE' | translate }}</h1>
<p class="desc">{{ 'USER.PASSWORD.DESCRIPTION' | translate }}</p>
<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" />
<mat-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"
type="password" />
<mat-error *ngIf="confirmPassword?.errors?.notequal">
{{ 'USER.PASSWORD.NOTEQUAL' | translate }}
</mat-error>
</mat-form-field>
</div>
<div class="btn-container">
<button class="submit-button" [disabled]="passwordForm.invalid" mat-raised-button
color="primary">{{ 'USER.PASSWORD.SET' | translate }}</button>
</div>
</form>
</ng-container>
<ng-container *ngIf="userId; else authUser">
<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" />
<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
formControlName="currentPassword" />
<mat-error *ngIf="currentPassword?.errors?.required">
{{ 'USER.PASSWORD.REQUIRED' | translate }}
</mat-error>
</mat-form-field>
<mat-error *ngIf="password?.errors?.required">
{{ 'USER.VALIDATION.REQUIRED' | translate }}
</mat-error>
<mat-error *ngIf="password?.errors?.symbolValidator">
{{ 'USER.VALIDATION.SYMBOLERROR' | translate }}
</mat-error>
<mat-error *ngIf="password?.errors?.numberValidator">
{{ 'USER.VALIDATION.NUMBERERROR' | translate }}
</mat-error>
<mat-error *ngIf="password?.errors?.upperCaseValidator">
{{ 'USER.VALIDATION.UPPERCASEMISSING' | translate }}
</mat-error>
<mat-error *ngIf="password?.errors?.lowerCaseValidator">
{{ 'USER.VALIDATION.LOWERCASEMISSING' | translate }}
</mat-error>
<mat-error *ngIf="password?.errors?.minlength">
{{ 'USER.VALIDATION.MINLENGTH' | translate:password?.errors?.minlength }}
</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"
type="password" />
<div class="validation between" *ngIf="this.policy">
<app-password-complexity-view [policy]="this.policy" [password]="newPassword">
</app-password-complexity-view>
</div>
<mat-error *ngIf="confirmPassword?.errors?.required">
{{ 'USER.VALIDATION.REQUIRED' | translate }}
</mat-error>
<mat-error *ngIf="confirmPassword?.errors?.symbolValidator">
{{ 'USER.VALIDATION.SYMBOLERROR' | translate }}
</mat-error>
<mat-error *ngIf="confirmPassword?.errors?.numberValidator">
{{ 'USER.VALIDATION.NUMBERERROR' | translate }}
</mat-error>
<mat-error *ngIf="confirmPassword?.errors?.notequal">
{{ 'USER.PASSWORD.NOTEQUAL' | translate }}
</mat-error>
<mat-error *ngIf="confirmPassword?.errors?.upperCaseValidator">
{{ 'USER.VALIDATION.UPPERCASEMISSING' | translate }}
</mat-error>
<mat-error *ngIf="confirmPassword?.errors?.lowerCaseValidator">
{{ 'USER.VALIDATION.LOWERCASEMISSING' | translate }}
</mat-error>
<mat-error *ngIf="confirmPassword?.errors?.minlength">
{{ 'USER.VALIDATION.MINLENGTH' | translate:confirmPassword?.errors?.minlength }}
</mat-error>
</mat-form-field>
</div>
<div class="btn-container">
<button class="submit-button" [disabled]="passwordForm.invalid" mat-raised-button
color="primary">{{ 'USER.PASSWORD.SET' | translate }}</button>
</div>
</form>
</ng-container>
<mat-form-field class="formfield" appearance="outline">
<mat-label>{{ 'USER.PASSWORD.NEW' | translate }}</mat-label>
<input autocomplete="off" name="new password" type="password" matInput
formControlName="newPassword" />
<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
formControlName="currentPassword" />
<mat-error *ngIf="currentPassword?.errors?.required">
{{ 'USER.PASSWORD.REQUIRED' | translate }}
</mat-error>
</mat-form-field>
<mat-form-field class="formfield" appearance="outline">
<mat-label>{{ 'USER.PASSWORD.NEW' | translate }}</mat-label>
<input autocomplete="off" name="new password" type="password" matInput
formControlName="newPassword" />
<mat-error *ngIf="newPassword?.errors?.required">
{{ 'USER.VALIDATION.REQUIRED' | translate }}
</mat-error>
<mat-error *ngIf="newPassword?.errors?.symbolValidator">
{{ 'USER.VALIDATION.SYMBOLERROR' | translate }}
</mat-error>
<mat-error *ngIf="newPassword?.errors?.numberValidator">
{{ 'USER.VALIDATION.NUMBERERROR' | translate }}
</mat-error>
<mat-error *ngIf="newPassword?.errors?.upperCaseValidator">
{{ 'USER.VALIDATION.UPPERCASEMISSING' | translate }}
</mat-error>
<mat-error *ngIf="newPassword?.errors?.lowerCaseValidator">
{{ 'USER.VALIDATION.LOWERCASEMISSING' | translate }}
</mat-error>
<mat-error *ngIf="newPassword?.errors?.minlength">
{{ 'USER.VALIDATION.MINLENGTH' | translate:newPassword?.errors?.minlength }}
</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
formControlName="confirmPassword" />
<mat-error *ngIf="confirmPassword?.errors?.required">
{{ 'USER.VALIDATION.REQUIRED' | translate }}
</mat-error>
<mat-error *ngIf="confirmPassword?.errors?.symbolValidator">
{{ 'USER.VALIDATION.SYMBOLERROR' | translate }}
</mat-error>
<mat-error *ngIf="confirmPassword?.errors?.numberValidator">
{{ 'USER.VALIDATION.NUMBERERROR' | translate }}
</mat-error>
<mat-error *ngIf="confirmPassword?.errors?.notequal">
{{ 'USER.PASSWORD.NOTEQUAL' | translate }}
</mat-error>
<mat-error *ngIf="confirmPassword?.errors?.upperCaseValidator">
{{ 'USER.VALIDATION.UPPERCASEMISSING' | translate }}
</mat-error>
<mat-error *ngIf="confirmPassword?.errors?.lowerCaseValidator">
{{ 'USER.VALIDATION.LOWERCASEMISSING' | translate }}
</mat-error>
<mat-error *ngIf="confirmPassword?.errors?.minlength">
{{ 'USER.VALIDATION.MINLENGTH' | translate:confirmPassword?.errors?.minlength }}
</mat-error>
</mat-form-field>
</div>
<button class="submit-button" [disabled]="passwordForm.invalid" mat-raised-button
color="primary">{{ 'USER.PASSWORD.RESET' | translate }}</button>
</form>
</ng-template>
</div>
</div>
</div>
<mat-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
formControlName="confirmPassword" />
<mat-error *ngIf="confirmPassword?.errors?.notequal">
{{ 'USER.PASSWORD.NOTEQUAL' | translate }}
</mat-error>
</mat-form-field>
</div>
<button class="submit-button" [disabled]="passwordForm.invalid" mat-raised-button
color="primary">{{ 'USER.PASSWORD.RESET' | translate }}</button>
</form>
</ng-template>
</app-detail-layout>
@@ -1,66 +1,34 @@
.container {
display: flex;
padding-bottom: 3rem;
.left {
width: 100px;
display: flex;
padding: 1rem;
justify-content: center;
a {
margin-top: .2rem;
}
}
.right {
flex: 1;
padding-top: 1rem;
.head {
display: flex;
align-items: center;
border-bottom: 1px solid #ffffff20;
margin-bottom: 2rem;
flex-wrap: wrap;
a {
display: block;
}
h1 {
font-size: 2rem;
margin-top: 0.2rem;
}
.desc {
width: 100%;
display: block;
font-size: .9rem;
color: #8795a1;
}
}
}
}
.content {
display: flex;
flex-direction: row;
flex-wrap: wrap;
margin: 0 -.5rem;
display: flex;
flex-direction: row;
flex-wrap: wrap;
margin: 0 -.5rem;
mat-form-field {
margin: 0 .5rem;
}
&.center {
align-items: center;
}
.formfield {
margin: 0 .5rem;
width: 100%;
max-width: 400px;
}
&.center {
align-items: center;
}
}
.validation {
&.between {
margin: 0 .5rem;
}
}
.submit-button {
margin-top: 100px;
display: block;
padding: 0.5rem 4rem;
border-radius: 0.5rem;
}
margin-top: 100px;
display: block;
padding: .5rem 4rem;
border-radius: .5rem;
}
.mat-error {
display: block;
}
@@ -1,6 +1,7 @@
import { Component, OnInit } from '@angular/core';
import { Component, OnDestroy } from '@angular/core';
import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms';
import { ActivatedRoute } from '@angular/router';
import { Subscription } from 'rxjs';
import { lowerCaseValidator, numberValidator, symbolValidator, upperCaseValidator } from 'src/app/pages/validators';
import { PasswordComplexityPolicy } from 'src/app/proto/generated/auth_pb';
import { AuthUserService } from 'src/app/services/auth-user.service';
@@ -27,12 +28,14 @@ function passwordConfirmValidator(c: AbstractControl): any {
templateUrl: './password.component.html',
styleUrls: ['./password.component.scss'],
})
export class PasswordComponent implements OnInit {
export class PasswordComponent implements OnDestroy {
userId: string = '';
public policy!: PasswordComplexityPolicy.AsObject;
public passwordForm!: FormGroup;
private formSub: Subscription = new Subscription();
constructor(
activatedRoute: ActivatedRoute,
private fb: FormBuilder,
@@ -73,7 +76,8 @@ export class PasswordComponent implements OnInit {
});
}
ngOnInit(): void {
public ngOnDestroy(): void {
this.formSub.unsubscribe();
}
setupForm(validators: Validators[]): void {
@@ -20,11 +20,15 @@ const routes: Routes = [
path: 'me',
component: AuthUserDetailComponent,
canActivate: [AuthGuard],
data: {
animation: 'HomePage',
},
},
{
path: 'me/password',
component: PasswordComponent,
canActivate: [AuthGuard],
data: { animation: 'AddPage' },
},
{
path: ':id',
@@ -32,6 +36,7 @@ const routes: Routes = [
canActivate: [AuthGuard, RoleGuard],
data: {
roles: ['user.read'],
animation: 'HomePage',
},
},
{
@@ -40,6 +45,7 @@ const routes: Routes = [
canActivate: [AuthGuard, RoleGuard],
data: {
roles: ['user.write'],
animation: 'AddPage',
},
},
];
@@ -17,7 +17,9 @@ import { CopyToClipboardModule } from 'src/app/directives/copy-to-clipboard/copy
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
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 { 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';
import { SharedModule } from 'src/app/modules/shared/shared.module';
import { UserGrantsModule } from 'src/app/modules/user-grants/user-grants.module';
@@ -72,6 +74,8 @@ import { UserMfaComponent } from './user-detail/user-mfa/user-mfa.component';
SharedModule,
RefreshTableModule,
CopyToClipboardModule,
DetailLayoutModule,
PasswordComplexityViewModule,
],
})
export class UserDetailModule { }
@@ -56,7 +56,7 @@
color="primary"
*ngIf="user.state === UserState.USERSTATE_INITIAL">{{ 'USER.PASSWORD.RESENDNOTIFICATION' | translate }}</button>
<a [routerLink]="['password']" mat-icon-button>
<mat-icon>chevron_right</mat-icon>
<mat-icon class="icon">chevron_right</mat-icon>
</a>
</div>
</div>
@@ -66,11 +66,12 @@
<ng-container *ngIf="!emailEditState; else emailEdit">
<div class="actions">
<span class="name">{{user?.email}}</span>
<mat-icon *ngIf="user?.isEmailVerified" color="primary" aria-hidden="false"
<mat-icon class="icon" *ngIf="user?.isEmailVerified" color="primary" aria-hidden="false"
aria-label="verified icon">
check_circle_outline</mat-icon>
<ng-container *ngIf="user?.email && !user?.isEmailVerified">
<mat-icon color="warn" aria-hidden="false" aria-label="not verified icon">highlight_off
<mat-icon class="icon" color="warn" aria-hidden="false" aria-label="not verified icon">
highlight_off
</mat-icon>
<a class="verify" matTooltip="{{'USER.LOGINMETHODS.EMAIL.RESEND' | translate}}"
(click)="resendVerification()">{{'USER.LOGINMETHODS.RESENDCODE' | translate}}</a>
@@ -79,7 +80,7 @@
<div class="actions">
<button (click)="emailEditState = true" mat-icon-button>
<mat-icon>edit</mat-icon>
<mat-icon class="icon">edit</mat-icon>
</button>
</div>
</ng-container>
@@ -89,7 +90,7 @@
<input matInput [(ngModel)]="user.email" />
</mat-form-field>
<button (click)="emailEditState = false" mat-icon-button>
<mat-icon>close</mat-icon>
<mat-icon class="icon">close</mat-icon>
</button>
<button [disabled]="!user.email" class="submit-button" type="button" color="primary"
(click)="saveEmail()" mat-raised-button>{{ 'ACTIONS.SAVE' | translate }}</button>
@@ -102,11 +103,12 @@
<ng-container *ngIf="!phoneEditState; else phoneEdit">
<div class="actions">
<span class="name">{{user?.phone}}</span>
<mat-icon *ngIf="user?.isPhoneVerified" color="primary" aria-hidden="false"
<mat-icon class="icon" *ngIf="user?.isPhoneVerified" color="primary" aria-hidden="false"
aria-label="verified icon">
check_circle_outline</mat-icon>
<ng-container *ngIf="user?.phone && !user?.isPhoneVerified">
<mat-icon color="warn" aria-hidden="false" aria-label="not verified icon">highlight_off
<mat-icon class="icon" color="warn" aria-hidden="false" aria-label="not verified icon">
highlight_off
</mat-icon>
<a class="verify" matTooltip="{{'USER.LOGINMETHODS.PHONE.RESEND' | translate}}"
(click)="resendPhoneVerification()">{{'USER.LOGINMETHODS.RESENDCODE' | translate}}</a>
@@ -115,7 +117,7 @@
<div class="actions">
<button (click)="phoneEditState = true" mat-icon-button>
<mat-icon>edit</mat-icon>
<mat-icon class="icon">edit</mat-icon>
</button>
</div>
</ng-container>
@@ -126,7 +128,7 @@
<input matInput [(ngModel)]="user.phone" />
</mat-form-field>
<button (click)="phoneEditState = false" mat-icon-button>
<mat-icon>close</mat-icon>
<mat-icon class="icon">close</mat-icon>
</button>
<button *ngIf="user.phone" color="warn" (click)="deletePhone()" mat-icon-button>
<i class="las la-trash"></i>
@@ -156,6 +158,6 @@
</div>
</div>
<app-changes [changeType]="ChangeType.USER" [id]="user.id"></app-changes>
<app-changes class="changes" [changeType]="ChangeType.USER" [id]="user.id"></app-changes>
</div>
</app-meta-layout>
@@ -6,121 +6,91 @@
padding-bottom: .5rem;
a {
display: block;
margin-right: 2rem;
display: block;
margin-right: 2rem;
}
h1 {
font-size: 2rem;
margin: 0;
font-weight: normal;
margin-right: 1rem;
font-size: 2rem;
margin: 0;
font-weight: normal;
margin-right: 1rem;
}
.fill-space {
flex: 1;
flex: 1;
}
.state-button {
border-radius: .5rem;
border-radius: .5rem;
}
}
.card-actions {
button {
border-radius: .5rem;
}
}
.content {
display: flex;
flex-direction: row;
flex-wrap: wrap;
margin: 0 -.5rem;
&.nowrap{
flex-wrap: nowrap;
}
&.center {
align-items: center;
}
mat-form-field {
flex: 1 1 33%;
margin: 0 .5rem;
}
}
.btn-container {
display: flex;
justify-content: flex-end;
margin: 0 -.5rem;
button {
border-radius: .5rem;
margin: 0 .5rem;
}
}
.method-col {
display: flex;
flex-direction: column;
margin: -.5rem;
.method-row {
display: flex;
align-items: center;
justify-content: space-evenly;
padding: .5rem;
border-bottom: 1px solid #ffffff20;
flex-wrap: wrap;
.label, .name {
margin-right: 1rem;
}
.actions {
flex: 1;
display: flex;
justify-content: flex-end;
align-items: center;
display: flex;
flex-direction: column;
margin: -.5rem;
.notify-change-pwd {
border-radius: .5rem;
}
}
.label {
flex: 1;
font-size: .9rem;
color: #818a8a;
}
mat-icon {
margin-right: .5rem;
}
.submit-button {
border-radius: .5rem;
}
.verify {
text-decoration: none;
font-size: .8rem;
color: #8795a1;
border-radius: .5rem;
cursor: pointer;
word-wrap: none;
white-space: nowrap;
margin-right: 1rem;
&:hover {
color: white;
text-decoration: underline;
}
}
.method-row {
display: flex;
align-items: center;
justify-content: space-evenly;
padding: .5rem;
border-bottom: 1px solid #ffffff20;
flex-wrap: wrap;
.label,
.name {
margin-right: 1rem;
}
.actions {
flex: 1;
display: flex;
justify-content: flex-end;
align-items: center;
.notify-change-pwd {
border-radius: .5rem;
}
}
.label {
flex: 1;
font-size: .9rem;
color: #818a8a;
}
.icon {
margin-right: .5rem;
}
.submit-button {
border-radius: .5rem;
}
.verify {
text-decoration: none;
font-size: .8rem;
color: #8795a1;
border-radius: .5rem;
cursor: pointer;
word-wrap: none;
white-space: nowrap;
margin-right: 1rem;
&:hover {
color: white;
text-decoration: underline;
}
}
}
}
.img-phone-email {
@@ -128,120 +98,35 @@
}
.side {
.details {
margin-bottom: 1rem;
border-bottom: 1px solid #81868a40;
padding-bottom: 1rem;
.row {
display: flex;
margin-bottom: 0.5rem;
align-items: center;
button {
display: none;
visibility: hidden;
}
&:hover {
button {
display: inline-block;
visibility: visible;
mat-icon {
font-size: 1.2rem;
}
}
}
.first {
flex: 1;
font-size: 0.8rem;
margin-right: 0.5rem;
white-space: nowrap;
}
.fill-space {
flex: 1;
}
.second {
font-size: 0.8rem;
text-overflow: ellipsis;
overflow: hidden;
margin-left: 1rem;
text-align: right;
}
.details {
margin-bottom: 1rem;
border-bottom: 1px solid #81868a40;
padding-bottom: 1rem;
a {
&:hover {
cursor: pointer;
text-decoration: underline;
}
}
}
.row {
display: flex;
margin-bottom: .5rem;
align-items: center;
.side-section {
color: #8795a1;
}
}
.details {
margin-bottom: 1rem;
border-bottom: 1px solid #81868a40;
padding-bottom: 1rem;
.row {
display: flex;
margin-bottom: 0.5rem;
align-items: center;
button {
display: none;
visibility: hidden;
}
&:hover {
button {
display: inline-block;
visibility: visible;
mat-icon {
font-size: 1.2rem;
}
}
}
.first {
flex: 1;
font-size: 0.8rem;
margin-right: 0.5rem;
}
.fill-space {
flex: 1;
}
.second {
font-size: 0.8rem;
text-overflow: ellipsis;
overflow: hidden;
margin-left: 1rem;
}
a {
&:hover {
cursor: pointer;
text-decoration: underline;
}
}
}
.side-section {
color: #8795a1;
}
}
app-changes {
.first {
flex: 1;
min-height: 0;
font-size: .8rem;
margin-right: .5rem;
white-space: nowrap;
}
.second {
font-size: .8rem;
text-overflow: ellipsis;
overflow: hidden;
margin-left: 1rem;
text-align: right;
}
}
}
.changes {
flex: 1;
min-height: 0;
}
}
@@ -1,6 +1,6 @@
<app-card title="{{'USER.MFA.TITLE' | translate}}" description="{{'USER.MFA.DESCRIPTION' | translate}}">
<app-refresh-table [loading]="loading$ | async" (refreshed)="getOTP()" [dataSize]="dataSource?.data?.length">
<table class="background-style" mat-table [dataSource]="dataSource">
<table class="table background-style" mat-table [dataSource]="dataSource">
<ng-container matColumnDef="type">
<th mat-header-cell *matHeaderCellDef> {{ 'USER.MFA.TABLETYPE' | translate }} </th>
<td mat-cell *matCellDef="let mfa"> {{'USER.MFA.TYPE.'+ mfa.type | translate}} </td>
@@ -1,43 +1,44 @@
.col {
display: flex;
flex-direction: column;
border-bottom: 1px solid #ffffff20;
margin-bottom: 1rem;
.row {
display: flex;
flex-direction: column;
border-bottom: 1px solid #ffffff20;
margin-bottom: 1rem;
justify-content: space-between;
padding: .5rem 0;
align-items: center;
.row {
display: flex;
justify-content: space-between;
padding: .5rem 0;
align-items: center;
span {
flex: 1;
}
span {
flex: 1;
}
}
}
.centered {
display: flex;
align-items: center;
display: flex;
align-items: center;
i {
margin-left: 1rem;
color: #5282c1;
}
i {
margin-left: 1rem;
color: #5282c1;
}
}
table {
width: 100%;
.table {
width: 100%;
td, th {
&:first-child {
padding-left: 0;
padding-right: 1rem;
}
&:last-child {
padding-right: 0;
}
td,
th {
&:first-child {
padding-left: 0;
padding-right: 1rem;
}
}
&:last-child {
padding-right: 0;
}
}
}
@@ -18,7 +18,7 @@
</ng-template>
<div class="table-wrapper">
<table class="background-style" mat-table [dataSource]="dataSource">
<table class="table background-style" mat-table [dataSource]="dataSource">
<ng-container matColumnDef="select">
<th mat-header-cell *matHeaderCellDef>
<mat-checkbox color="primary" (change)="$event ? masterToggle() : null"
@@ -66,8 +66,8 @@
</tr>
</table>
<mat-paginator #paginator class="background-style" [length]="userResult?.totalResult || 0" [pageSize]="10"
[pageSizeOptions]="[5, 10, 20]" (page)="changePage($event)"></mat-paginator>
<mat-paginator #paginator class="paginator background-style" [length]="userResult?.totalResult || 0"
[pageSize]="10" [pageSizeOptions]="[5, 10, 20]" (page)="changePage($event)"></mat-paginator>
</div>
</app-refresh-table>
</div>
@@ -1,44 +1,47 @@
h1 {
margin-top: 0;
margin-top: 0;
}
.sub {
color: #8795a1;
margin-bottom: 2rem;
color: #8795a1;
margin-bottom: 2rem;
}
.add-button {
border-radius: .5rem;
border-radius: .5rem;
}
.table-wrapper {
overflow: auto;
overflow: auto;
table, mat-paginator {
width: 100%;
.table,
.paginator {
width: 100%;
td, th {
padding: 0 1rem;
td,
th {
padding: 0 1rem;
&:first-child {
padding-left: 0;
padding-right: 1rem;
}
&:first-child {
padding-left: 0;
padding-right: 1rem;
}
&:last-child {
padding-right: 0;
}
}
.data-row {
cursor: pointer;
&:hover {
background-color: #ffffff05;
}
}
&:last-child {
padding-right: 0;
}
}
.data-row {
cursor: pointer;
&:hover {
background-color: #ffffff05;
}
}
}
}
tr {
outline: none;
outline: none;
}