Browse Source

UI: Redesign login pages

pull/14562/head
Vladyslav_Prykhodko 6 months ago
parent
commit
2f9d142cc9
  1. 86
      ui-ngx/src/app/modules/login/pages/login/create-password.component.html
  2. 1
      ui-ngx/src/app/modules/login/pages/login/create-password.component.scss
  3. 14
      ui-ngx/src/app/modules/login/pages/login/create-password.component.ts
  4. 22
      ui-ngx/src/app/modules/login/pages/login/force-two-factor-auth-login.component.html
  5. 1
      ui-ngx/src/app/modules/login/pages/login/force-two-factor-auth-login.component.scss
  6. 7
      ui-ngx/src/app/modules/login/pages/login/link-expired.component.html
  7. 1
      ui-ngx/src/app/modules/login/pages/login/link-expired.component.scss
  8. 16
      ui-ngx/src/app/modules/login/pages/login/link-expired.component.ts
  9. 114
      ui-ngx/src/app/modules/login/pages/login/login.component.html
  10. 78
      ui-ngx/src/app/modules/login/pages/login/login.component.scss
  11. 25
      ui-ngx/src/app/modules/login/pages/login/login.component.ts
  12. 48
      ui-ngx/src/app/modules/login/pages/login/reset-password-request.component.html
  13. 1
      ui-ngx/src/app/modules/login/pages/login/reset-password-request.component.scss
  14. 5
      ui-ngx/src/app/modules/login/pages/login/reset-password-request.component.ts
  15. 90
      ui-ngx/src/app/modules/login/pages/login/reset-password.component.html
  16. 6
      ui-ngx/src/app/modules/login/pages/login/reset-password.component.scss
  17. 17
      ui-ngx/src/app/modules/login/pages/login/reset-password.component.ts
  18. 4
      ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.html
  19. 1
      ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.scss
  20. 1
      ui-ngx/src/assets/locale/locale.constant-ar_AE.json
  21. 1
      ui-ngx/src/assets/locale/locale.constant-ca_ES.json
  22. 1
      ui-ngx/src/assets/locale/locale.constant-cs_CZ.json
  23. 1
      ui-ngx/src/assets/locale/locale.constant-de_DE.json
  24. 1
      ui-ngx/src/assets/locale/locale.constant-el_GR.json
  25. 15
      ui-ngx/src/assets/locale/locale.constant-en_US.json
  26. 1
      ui-ngx/src/assets/locale/locale.constant-es_ES.json
  27. 1
      ui-ngx/src/assets/locale/locale.constant-fa_IR.json
  28. 1
      ui-ngx/src/assets/locale/locale.constant-fr_FR.json
  29. 1
      ui-ngx/src/assets/locale/locale.constant-it_IT.json
  30. 1
      ui-ngx/src/assets/locale/locale.constant-ja_JP.json
  31. 1
      ui-ngx/src/assets/locale/locale.constant-ka_GE.json
  32. 1
      ui-ngx/src/assets/locale/locale.constant-ko_KR.json
  33. 1
      ui-ngx/src/assets/locale/locale.constant-lt_LT.json
  34. 1
      ui-ngx/src/assets/locale/locale.constant-lv_LV.json
  35. 1
      ui-ngx/src/assets/locale/locale.constant-nl_BE.json
  36. 1
      ui-ngx/src/assets/locale/locale.constant-nl_NL.json
  37. 1
      ui-ngx/src/assets/locale/locale.constant-no_NO.json
  38. 1
      ui-ngx/src/assets/locale/locale.constant-pl_PL.json
  39. 1
      ui-ngx/src/assets/locale/locale.constant-pt_BR.json
  40. 1
      ui-ngx/src/assets/locale/locale.constant-ro_RO.json
  41. 1
      ui-ngx/src/assets/locale/locale.constant-sl_SI.json
  42. 1
      ui-ngx/src/assets/locale/locale.constant-tr_TR.json
  43. 1
      ui-ngx/src/assets/locale/locale.constant-uk_UA.json
  44. 1
      ui-ngx/src/assets/locale/locale.constant-zh_CN.json
  45. 1
      ui-ngx/src/assets/locale/locale.constant-zh_TW.json
  46. 33
      ui-ngx/src/styles.scss

86
ui-ngx/src/app/modules/login/pages/login/create-password.component.html

@ -17,55 +17,49 @@
-->
<div class="tb-create-password-content mat-app-background tb-dark flex flex-row items-center justify-center" style="width: 100%;">
<mat-card appearance="raised" class="tb-create-password-card flex-initial">
<mat-card-header>
<mat-card-title class="layout-padding">
<span translate class="mat-headline-5">login.create-password</span>
</mat-card-title>
<mat-card-header class="px-6 pt-6">
<mat-card-title class="text-xl font-normal" translate>login.create-password</mat-card-title>
</mat-card-header>
<mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async">
<div class="h-3 px-4"><div tb-toast></div></div>
<mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading">
</mat-progress-bar>
<span style="height: 4px;" *ngIf="!(isLoading$ | async)"></span>
<mat-card-content>
<span style="height: 4px;" *ngIf="!isLoading"></span>
<mat-card-content class="!p-6">
<form [formGroup]="createPassword" (ngSubmit)="onCreatePassword()">
<fieldset [disabled]="isLoading$ | async">
<div tb-toast class="layout-padding flex flex-col">
<span style="height: 50px;"></span>
<mat-form-field class="mat-block tb-appearance-transparent">
<mat-label translate>common.password</mat-label>
<input matInput
type="password"
autofocus
cdkOverlayOrigin
#passwordTrigger="cdkOverlayOrigin"
(focus)="passwordTooltip.onFocus()"
(blur)="passwordTooltip.onBlur()"
formControlName="newPassword"/>
<mat-icon class="material-icons" matPrefix>lock</mat-icon>
<tb-toggle-password matSuffix></tb-toggle-password>
<mat-error *ngIf="this.createPassword.get('newPassword').errors">
{{ 'security.password-requirement.password-not-meet-requirements' | translate }}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block tb-appearance-transparent">
<mat-label translate>login.password-again</mat-label>
<input matInput type="password" formControlName="newPassword2"/>
<mat-icon class="material-icons" matPrefix>lock</mat-icon>
<tb-toggle-password matSuffix></tb-toggle-password>
<mat-error *ngIf="createPassword.get('newPassword2').hasError('passwordsNotMatch')">
{{ 'security.password-requirement.new-passwords-not-match' | translate }}
</mat-error>
</mat-form-field>
<div class="flex flex-col items-center justify-start gap-4 gt-xs:flex-row gt-xs:items-start gt-xs:justify-center">
<button mat-raised-button color="accent" type="submit" [disabled]="(isLoading$ | async)">
{{ 'login.create-password' | translate }}
</button>
<button mat-button type="button" [disabled]="(isLoading$ | async)"
routerLink="/login">
{{ 'action.cancel' | translate }}
</button>
</div>
</div>
</fieldset>
<mat-form-field class="mat-block" appearance="outline">
<mat-label translate>common.password</mat-label>
<input matInput
type="password"
autofocus
cdkOverlayOrigin
autocomplete="new-password"
#passwordTrigger="cdkOverlayOrigin"
(focus)="passwordTooltip.onFocus()"
(blur)="passwordTooltip.onBlur()"
formControlName="newPassword"/>
<mat-icon class="material-icons" matPrefix>lock</mat-icon>
<tb-toggle-password matSuffix></tb-toggle-password>
<mat-error *ngIf="this.createPassword.get('newPassword').errors">
{{ 'security.password-requirement.password-not-meet-requirements' | translate }}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block" style="margin-bottom: 18px" appearance="outline">
<mat-label translate>login.password-again</mat-label>
<input matInput type="password" formControlName="newPassword2"/>
<mat-icon class="material-icons" matPrefix>lock</mat-icon>
<tb-toggle-password matSuffix></tb-toggle-password>
<mat-error *ngIf="createPassword.get('newPassword2').hasError('passwordsNotMatch')">
{{ 'security.password-requirement.new-passwords-not-match' | translate }}
</mat-error>
</mat-form-field>
<div class="flex flex-col items-center justify-start gap-4 gt-xs:flex-row gt-xs:items-start gt-xs:justify-center">
<button mat-flat-button color="accent" type="submit" [disabled]="isLoading">
{{ 'login.create-password' | translate }}
</button>
<button mat-button type="button" [disabled]="isLoading" routerLink="/login">
{{ 'action.cancel' | translate }}
</button>
</div>
</form>
</mat-card-content>
</mat-card>

1
ui-ngx/src/app/modules/login/pages/login/create-password.component.scss

@ -20,6 +20,7 @@
flex: 1 1 0;
.tb-create-password-content {
background-color: #eee;
--mdc-elevated-card-container-elevation: none;
.tb-create-password-card {
@media #{$mat-gt-xs} {
width: 450px !important;

14
ui-ngx/src/app/modules/login/pages/login/create-password.component.ts

@ -16,7 +16,6 @@
import { Component } from '@angular/core';
import { AuthService } from '@core/auth/auth.service';
import { PageComponent } from '@shared/components/page.component';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { ActivatedRoute } from '@angular/router';
import { UserPasswordPolicy } from '@shared/models/settings.models';
@ -27,17 +26,18 @@ import { passwordsMatchValidator, passwordStrengthValidator } from '@shared/mode
templateUrl: './create-password.component.html',
styleUrls: ['./create-password.component.scss']
})
export class CreatePasswordComponent extends PageComponent {
export class CreatePasswordComponent {
passwordPolicy: UserPasswordPolicy;
createPassword: FormGroup;
isLoading = false;
private activateToken: string;
constructor(private route: ActivatedRoute,
private authService: AuthService,
private fb: FormBuilder) {
super();
this.activateToken = this.route.snapshot.queryParams['activateToken'] || '';
this.passwordPolicy = this.route.snapshot.data['passwordPolicy'];
@ -60,9 +60,11 @@ export class CreatePasswordComponent extends PageComponent {
if (this.createPassword.invalid) {
this.createPassword.markAllAsTouched();
} else {
this.authService.activate(
this.activateToken,
this.createPassword.get('newPassword').value, true).subscribe();
this.isLoading = true
this.authService.activate(this.activateToken, this.createPassword.get('newPassword').value, true)
.subscribe({
error: () => {this.isLoading = false;}
});
}
}
}

22
ui-ngx/src/app/modules/login/pages/login/force-two-factor-auth-login.component.html

@ -41,7 +41,7 @@
</button>
}
@if (config) {
<button type="button" mat-raised-button color="accent" class="navigation w-full" (click)="cancelLogin()">
<button type="button" mat-flat-button color="accent" class="navigation w-full" (click)="cancelLogin()">
{{ 'login.login' | translate }}
</button>
}
@ -66,7 +66,7 @@
<p class="mat-body qr-code-description mb-4" translate>login.scan-qr-code</p>
<canvas class="flex-1" #canvas [style.display]="totpAuthURL ? 'block' : 'none'"></canvas>
<p class="mat-body qr-code-description" translate>login.enter-key-manually</p>
<div class="flex flex-row items-center mb-8 w-full overflow-hidden">
<div class="mb-8 flex w-full flex-row items-center overflow-hidden">
<span tbTruncateWithTooltip class="w-full">{{ totpAuthURLSecret }}</span>
<tb-copy-button
class="attribute-copy"
@ -79,7 +79,7 @@
>
</tb-copy-button>
</div>
<div class="flex flex-col items-center justify-start gap-2 w-full">
<div class="flex w-full flex-col items-center justify-start gap-2">
<button type="button" mat-stroked-button class="navigation w-full" (click)="appState.set(ProvidersState.ENTER_CODE)">
{{ 'login.continue' | translate }}
</button>
@ -126,7 +126,7 @@
</tb-phone-input>
</div>
</form>
<div class="flex flex-col items-center justify-start gap-2 w-full">
<div class="flex w-full flex-col items-center justify-start gap-2">
<button type="button" mat-stroked-button [disabled]="(isLoading$ | async) || smsConfigForm.invalid || !smsConfigForm.dirty" class="navigation w-full" (click)="sendSmsCode()">
{{ 'login.send-code' | translate }}
</button>
@ -162,7 +162,7 @@
<div tb-toast class="flex flex-col items-center justify-start">
<form [formGroup]="emailConfigForm" class="mb-8 w-full">
<p class="mat-body step-description input" translate>login.email-description</p>
<mat-form-field class="mat-block input-container flex-1">
<mat-form-field class="mat-block input-container flex-1" appearance="outline">
<input matInput formControlName="email"
type="email" required
placeholder="{{ 'login.email-label' | translate }}" />
@ -174,7 +174,7 @@
</mat-error>
</mat-form-field>
</form>
<div class="flex flex-col items-center justify-start gap-2 w-full">
<div class="flex w-full flex-col items-center justify-start gap-2">
<button type="button" mat-stroked-button [disabled]="(isLoading$ | async) || emailConfigForm.invalid" class="navigation w-full" (click)="sendEmailCode()">
{{ 'login.send-code' | translate }}
</button>
@ -223,7 +223,7 @@
</button>
</div>
<p class="mat-body-2 description" translate>login.backup-code-warn</p>
<button type="button" mat-raised-button color="accent" class="navigation w-full" (click)="backupCodeState.set(BackupCodeState.SUCCESS)">
<button type="button" mat-flat-button color="accent" class="navigation w-full" (click)="backupCodeState.set(BackupCodeState.SUCCESS)">
{{ 'login.continue' | translate }}
</button>
</div>
@ -259,7 +259,7 @@
}
</p>
<form [formGroup]="configForm" class="flex flex-col items-center justify-start">
<mat-form-field class="mat-block w-full">
<mat-form-field class="mat-block w-full" appearance="outline">
<input matInput formControlName="verificationCode"
maxlength="6" type="text" required
inputmode="numeric" pattern="[0-9]*"
@ -278,7 +278,7 @@
<mat-error>{{ 'login.verification-code-many-request' | translate }}</mat-error>
}
</mat-form-field>
<div class="flex flex-col items-center justify-start gap-2 w-full">
<div class="flex w-full flex-col items-center justify-start gap-2">
<button type="button" mat-flat-button color="accent" [disabled]="(isLoading$ | async) || configForm.invalid || !configForm.dirty" class="navigation w-full" (click)="saveConfig(providerType)">
{{ 'login.confirm' | translate }}
</button>
@ -300,8 +300,8 @@
<mat-card-content>
<p class="mat-body mb-16" translate>{{ twoFactorAuthProvidersSuccessCardTranslate.get(providerType).description | translate }}</p>
<div class="flex flex-col items-center justify-start">
<div class="flex flex-col items-center justify-start gap-2 w-full">
<button type="button" mat-raised-button color="accent" class="navigation w-full" (click)="cancelLogin()">
<div class="flex w-full flex-col items-center justify-start gap-2">
<button type="button" mat-flat-button color="accent" class="navigation w-full" (click)="cancelLogin()">
{{ 'login.login' | translate }}
</button>
@if (isAnyProviderAvailable) {

1
ui-ngx/src/app/modules/login/pages/login/force-two-factor-auth-login.component.scss

@ -23,6 +23,7 @@
.tb-two-factor-auth-login-content {
background-color: #eee;
--mdc-elevated-card-container-elevation: none;
.progress-bar {
z-index: 10;

7
ui-ngx/src/app/modules/login/pages/login/link-expired.component.html

@ -23,16 +23,11 @@
<span class="mat-headline-5">{{ title | translate }}</span>
</mat-card-title>
</mat-card-header>
<mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async">
</mat-progress-bar>
<span style="height: 4px;" *ngIf="!(isLoading$ | async)"></span>
<mat-card-content style="padding: 24px 16px">
<div>{{ message | translate }}</div>
</mat-card-content>
<mat-card-actions class="flex flex-row justify-center">
<button mat-raised-button color="accent"
[disabled]="(isLoading$ | async)"
(click)="navigateToLoginPage()">
<button mat-flat-button color="accent" routerLink="/login">
{{ 'login.login' | translate }}
</button>
</mat-card-actions>

1
ui-ngx/src/app/modules/login/pages/login/link-expired.component.scss

@ -20,6 +20,7 @@
flex: 1 1 0;
.tb-expired-link-content {
background-color: #eee;
--mdc-elevated-card-container-elevation: none;
.tb-expired-link-card {
letter-spacing: 0.15px;
line-height: 24px;

16
ui-ngx/src/app/modules/login/pages/login/link-expired.component.ts

@ -15,33 +15,23 @@
///
import { Component } from '@angular/core';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { PageComponent } from '@shared/components/page.component';
import { ActivatedRoute, Router } from '@angular/router';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'tb-link-expired',
templateUrl: './link-expired.component.html',
styleUrls: ['./link-expired.component.scss']
})
export class LinkExpiredComponent extends PageComponent {
export class LinkExpiredComponent {
isPasswordLinkExpired: boolean;
title: string;
message: string;
constructor(protected store: Store<AppState>,
private route: ActivatedRoute,
private router: Router) {
super(store);
constructor(private route: ActivatedRoute) {
this.isPasswordLinkExpired = this.route.snapshot.data.passwordLinkExpired;
this.title = this.isPasswordLinkExpired ? 'login.reset-password-link-expired' : 'login.activation-link-expired';
this.message = this.isPasswordLinkExpired ? 'login.reset-password-link-expired-message' :
'login.activation-link-expired-message';
}
navigateToLoginPage() {
this.router.navigateByUrl('login');
}
}

114
ui-ngx/src/app/modules/login/pages/login/login.component.html

@ -17,71 +17,67 @@
-->
<div class="tb-login-content mat-app-background tb-dark flex flex-1 items-center justify-center">
<mat-card appearance="raised" style="max-height: 80vh; overflow-y: auto;">
<mat-card-content>
<form class="tb-login-form" [formGroup]="loginFormGroup" (ngSubmit)="login()">
<fieldset class="flex flex-col">
<div class="flex flex-col items-center justify-start" style="padding: 15px 0;">
<tb-logo link="https://thingsboard.io" target="_blank" class="login-logo"></tb-logo>
</div>
<mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async">
</mat-progress-bar>
<span style="height: 4px;" *ngIf="!(isLoading$ | async)"></span>
<div tb-toast class="layout-padding flex flex-col">
<span style="height: 50px;"></span>
@if(oauth2Clients?.length) {
<div class="oauth-container tb-default flex flex-col gap-4">
@if(oauth2Clients.length === 1) {
<a mat-stroked-button class="login-with-button" [href]=getOAuth2Uri(oauth2Clients[0])>
<tb-icon matButtonIcon class="tb-mat-20">{{ oauth2Clients[0].icon }}</tb-icon>
{{ 'login.login-with' | translate: {name: oauth2Clients[0].name} }}
</a>
} @else {
<div class="login-button-container flex flex-wrap justify-center gap-4">
@for(oauth2Client of oauth2Clients; track oauth2Client.url) {
<a mat-stroked-button class="login-with-button multiple"
[matTooltip]="('login.login-with' | translate: {name: oauth2Client.name})"
[href]=getOAuth2Uri(oauth2Client)>
@if(oauth2Client.icon) {
<tb-icon class="tb-mat-20">{{ oauth2Client.icon }}</tb-icon>
} @else {
<span>{{ oauth2Client.name }}</span>
}
</a>
<mat-card-content class="!p-6">
<form class="tb-login-form flex flex-col" [formGroup]="loginFormGroup" (ngSubmit)="login()">
<div class="flex flex-col items-center justify-start" style="padding: 16px 0;">
<tb-logo link="https://thingsboard.io" target="_blank" class="login-logo"></tb-logo>
</div>
<mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading">
</mat-progress-bar>
<span style="height: 4px;" *ngIf="!isLoading"></span>
<span tb-toast style="height: 20px;"></span>
<div class="flex flex-col">
@if(oauth2Clients?.length) {
<div class="oauth-container tb-default flex flex-col gap-4">
@if(oauth2Clients.length !== 1) {
<div class="title">{{ 'login.sign-in-with' | translate: {name: ''} }}</div>
}
<div class="login-button-container flex flex-wrap justify-center gap-4">
@for(oauth2Client of oauth2Clients; track oauth2Client.url) {
<a mat-stroked-button class="login-with-button"
[href]=getOAuth2Uri(oauth2Client)>
<tb-icon matButtonIcon class="tb-mat-20">{{ oauth2Client.icon }}</tb-icon>
@if(oauth2Clients.length === 1) {
{{ 'login.sign-in-with' | translate: {name: oauth2Clients[0].name} }}
} @else {
{{ oauth2Client.name }}
}
</div>
</a>
}
<div class="container-divider">
<div class="line"><mat-divider></mat-divider></div>
<div class="text">{{ "login.or" | translate | uppercase }}</div>
<div class="line"><mat-divider></mat-divider></div>
</div>
</div>
}
<mat-form-field class="tb-appearance-transparent">
<mat-label translate>login.username</mat-label>
<input id="username-input" matInput type="email" autofocus formControlName="username"/>
<mat-icon matPrefix>email</mat-icon>
<mat-error *ngIf="loginFormGroup.get('username').invalid">
{{ 'login.invalid-email-format' | translate }}
</mat-error>
</mat-form-field>
<mat-form-field class="tb-appearance-transparent">
<mat-label translate>common.password</mat-label>
<input id="password-input" matInput type="password" formControlName="password"/>
<tb-toggle-password matSuffix></tb-toggle-password>
<mat-icon matPrefix>lock</mat-icon>
</mat-form-field>
<div class="forgot-password flex items-center justify-end">
<button class="tb-reset-password" mat-button type="button" routerLink="/login/resetPasswordRequest">
{{ (passwordViolation ? 'login.reset-password' : 'login.forgot-password') | translate }}
</button>
<div class="container-divider">
<mat-divider class="tb-dark flex-1"></mat-divider>
<div class="text">{{ "login.or" | translate | uppercase }}</div>
<mat-divider class="tb-dark flex-1"></mat-divider>
</div>
</div>
<div class="tb-action-button flex flex-col">
<button mat-raised-button color="accent" [disabled]="(isLoading$ | async)"
type="submit">{{ 'login.login' | translate }}</button>
} @else {
<div class="oauth-container flex flex-col">
<div class="title !mb-4" translate>login.sign-in-to-your-account</div>
</div>
}
<mat-form-field class="tb-autofilled" appearance="outline" hideRequiredMarker>
<mat-label translate>login.username</mat-label>
<input id="username-input" matInput type="email" autofocus formControlName="username" autocomplete="username"/>
<mat-icon matPrefix>email</mat-icon>
<mat-error *ngIf="loginFormGroup.get('username').invalid">
{{ 'login.invalid-email-format' | translate }}
</mat-error>
</mat-form-field>
<mat-form-field class="tb-autofilled" appearance="outline">
<mat-label translate>common.password</mat-label>
<input id="password-input" matInput type="password" formControlName="password" autocomplete="current-password"/>
<tb-toggle-password matSuffix></tb-toggle-password>
<mat-icon matPrefix>lock</mat-icon>
</mat-form-field>
<button mat-flat-button color="accent" [disabled]="isLoading"
class="tb-action-button" type="submit">{{ 'login.sign-in' | translate }}</button>
<div class="forgot-password flex items-center justify-end gap-5">
<button class="tb-reset-password" mat-button type="button" routerLink="/login/resetPasswordRequest">
{{ (passwordViolation ? 'login.reset-password' : 'login.forgot-password') | translate }}
</button>
</div>
</fieldset>
</div>
</form>
</mat-card-content>
</mat-card>

78
ui-ngx/src/app/modules/login/pages/login/login.component.scss

@ -23,48 +23,49 @@
margin-top: 36px;
margin-bottom: 76px;
background-color: #eee;
--mdc-elevated-card-container-elevation: none;
.tb-login-form {
@media #{$mat-gt-xs} {
width: 550px !important;
width: 480px !important;
}
.forgot-password {
padding: 0 0.5em 1em;
.tb-reset-password {
padding: 0 6px;
}
--mat-text-button-horizontal-padding: 0;
--mdc-text-button-container-height: 20px;
}
.tb-action-button{
padding: 20px 0 16px;
margin: 20px 0 16px;
}
}
.oauth-container{
padding: 0;
.container-divider {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
width: 100%;
.container-divider {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
width: 100%;
margin-bottom: 16px;
.line {
flex: 1;
}
.mat-divider-horizontal{
--mat-divider-color: var(--mdc-outlined-text-field-outline-color);
}
.mat-divider-horizontal{
position: relative;
}
.text {
padding-right: 8px;
padding-left: 8px;
font-size: 16px;
line-height: 24px;
}
}
.text {
padding-right: 8px;
padding-left: 8px;
font-size: 16px;
line-height: 24px;
letter-spacing: 0.15px;
}
.oauth-container{
.title {
font-size: 20px;
line-height: 24px;
letter-spacing: .1px;
text-align: center;
margin-bottom: 4px;
}
.material-icons{
@ -74,33 +75,32 @@
a.login-with-button {
color: rgba(black, 0.87);
background-color: map-get($tb-dark-theme-background, raised-button);
background-color: #ffffff;
}
.login-button-container {
a.login-with-button {
--mdc-outlined-button-container-shape: 8px;
max-width: 123px;
max-width: 170px;
min-width: 60px;
flex-grow: 1;
flex-basis: 120px;
flex-basis: 130px;
.tb-mat-20 {
margin: 0;
vertical-align: text-top;
margin-right: 12px;
}
}
&:has(> :nth-child(2):last-child) {
&:has(> :nth-child(1):last-child) {
a.login-with-button {
max-width: 180px;
flex-basis: 180px;
max-width: 100%;
}
}
&:has(> :nth-child(3):last-child) {
&:has(> :nth-child(2):last-child),
&:has(> :nth-child(4):last-child) {
a.login-with-button {
max-width: 180px;
max-width: 100%;
flex-basis: 180px;
}
}
}

25
ui-ngx/src/app/modules/login/pages/login/login.component.ts

@ -16,9 +16,6 @@
import { Component, OnInit } from '@angular/core';
import { AuthService } from '@core/auth/auth.service';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { PageComponent } from '@shared/components/page.component';
import { UntypedFormBuilder, Validators } from '@angular/forms';
import { HttpErrorResponse } from '@angular/common/http';
import { Constants } from '@shared/models/constants';
@ -31,9 +28,10 @@ import { validateEmail } from '@app/core/utils';
templateUrl: './login.component.html',
styleUrls: ['./login.component.scss']
})
export class LoginComponent extends PageComponent implements OnInit {
export class LoginComponent implements OnInit {
passwordViolation = false;
isLoading = false;
loginFormGroup = this.fb.group({
username: ['', [Validators.required, validateEmail]],
@ -41,11 +39,9 @@ export class LoginComponent extends PageComponent implements OnInit {
});
oauth2Clients: Array<OAuth2ClientLoginInfo> = null;
constructor(protected store: Store<AppState>,
private authService: AuthService,
constructor(private authService: AuthService,
public fb: UntypedFormBuilder,
private router: Router) {
super(store);
}
ngOnInit() {
@ -54,9 +50,11 @@ export class LoginComponent extends PageComponent implements OnInit {
login(): void {
if (this.loginFormGroup.valid) {
this.authService.login(this.loginFormGroup.value).subscribe(
() => {},
(error: HttpErrorResponse) => {
this.isLoading = true;
this.authService.login(this.loginFormGroup.value).subscribe({
next: () => {},
error: (error: HttpErrorResponse) => {
this.isLoading = false;
if (error && error.error && error.error.errorCode) {
if (error.error.errorCode === Constants.serverErrorCode.credentialsExpired) {
this.router.navigateByUrl(`login/resetExpiredPassword?resetToken=${error.error.resetToken}`);
@ -65,12 +63,9 @@ export class LoginComponent extends PageComponent implements OnInit {
}
}
}
);
} else {
Object.keys(this.loginFormGroup.controls).forEach(field => {
const control = this.loginFormGroup.get(field);
control.markAsTouched({onlySelf: true});
});
} else {
this.loginFormGroup.markAllAsTouched();
}
}

48
ui-ngx/src/app/modules/login/pages/login/reset-password-request.component.html

@ -18,38 +18,32 @@
<div class="tb-request-password-reset-content mat-app-background tb-dark flex flex-row items-center justify-center"
style="width: 100%;">
<mat-card appearance="raised" class="tb-request-password-reset-card flex flex-initial">
<mat-card-header>
<mat-card-title class="layout-padding">
<span translate class="mat-headline-5">login.request-password-reset</span>
</mat-card-title>
<mat-card-header class="px-6 pt-6">
<mat-card-title class="text-xl font-normal" translate>login.request-password-reset</mat-card-title>
</mat-card-header>
<div class="h-3 px-4"><div tb-toast></div></div>
<mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async">
</mat-progress-bar>
<span style="height: 4px;" *ngIf="!(isLoading$ | async)"></span>
<mat-card-content>
<mat-card-content class="!p-6">
<form [formGroup]="requestPasswordRequest" (ngSubmit)="sendResetPasswordLink()">
<fieldset [disabled]="isLoading$ | async">
<div tb-toast class="layout-padding flex flex-col">
<span style="height: 50px;"></span>
<mat-form-field class="mat-block tb-appearance-transparent" hideRequiredMarker>
<mat-label translate>login.email</mat-label>
<input matInput type="email" autofocus formControlName="email"/>
<mat-icon class="material-icons" matPrefix>email</mat-icon>
<mat-error *ngIf="requestPasswordRequest.get('email').invalid">
{{ 'user.invalid-email-format' | translate }}
</mat-error>
</mat-form-field>
<div class="flex flex-col items-center justify-start gap-4 gt-xs:flex-row gt-xs:items-start gt-xs:justify-center">
<button mat-raised-button color="accent" type="submit" [disabled]="(isLoading$ | async) || this.clicked">
{{ 'login.request-password-reset' | translate }}
</button>
<button mat-button type="button" [disabled]="(isLoading$ | async)"
routerLink="/login">
{{ 'action.cancel' | translate }}
</button>
</div>
</div>
</fieldset>
<mat-form-field class="mat-block" style="margin-bottom: 18px" appearance="outline" hideRequiredMarker>
<mat-label translate>login.email</mat-label>
<input matInput type="email" autofocus formControlName="email"/>
<mat-icon class="material-icons" matPrefix>email</mat-icon>
<mat-error *ngIf="requestPasswordRequest.get('email').invalid">
{{ 'user.invalid-email-format' | translate }}
</mat-error>
</mat-form-field>
<div class="flex flex-col items-center justify-start gap-4 gt-xs:flex-row gt-xs:items-start gt-xs:justify-center">
<button mat-flat-button color="accent" type="submit" [disabled]="(isLoading$ | async) || this.clicked">
{{ 'login.request-password-reset' | translate }}
</button>
<button mat-button type="button" [disabled]="(isLoading$ | async)"
routerLink="/login">
{{ 'action.cancel' | translate }}
</button>
</div>
</form>
</mat-card-content>
</mat-card>

1
ui-ngx/src/app/modules/login/pages/login/reset-password-request.component.scss

@ -20,6 +20,7 @@
flex: 1 1 0;
.tb-request-password-reset-content {
background-color: #eee;
--mdc-elevated-card-container-elevation: none;
.tb-request-password-reset-card {
@media #{$mat-gt-xs} {
width: 450px !important;

5
ui-ngx/src/app/modules/login/pages/login/reset-password-request.component.ts

@ -29,7 +29,7 @@ import { validateEmail } from '@app/core/utils';
templateUrl: './reset-password-request.component.html',
styleUrls: ['./reset-password-request.component.scss']
})
export class ResetPasswordRequestComponent extends PageComponent implements OnInit {
export class ResetPasswordRequestComponent extends PageComponent {
clicked: boolean = false;
@ -44,9 +44,6 @@ export class ResetPasswordRequestComponent extends PageComponent implements OnIn
super(store);
}
ngOnInit() {
}
disableInputs() {
this.requestPasswordRequest.disable();
this.clicked = true;

90
ui-ngx/src/app/modules/login/pages/login/reset-password.component.html

@ -17,58 +17,52 @@
-->
<div class="tb-reset-password-content mat-app-background tb-dark flex flex-row items-center justify-center" style="width: 100%;">
<mat-card appearance="raised" class="tb-reset-password-card flex-initial">
<mat-card-header>
<mat-card-title class="layout-padding tb-card-title mb-4">
<span translate class="mat-headline-5 tb-card-title">login.password-reset</span>
</mat-card-title>
<mat-card-subtitle *ngIf="isExpiredPassword" class="layout-padding tb-card-title">
<div class="tb-card-title whitespace-pre-line">{{ 'login.expired-password-reset-message' | translate }}</div>
<mat-card-header class="px-6 pt-6">
<mat-card-title class="text-xl font-normal" translate>login.password-reset</mat-card-title>
<mat-card-subtitle *ngIf="isExpiredPassword" class="mt-4 whitespace-pre-line text-sm font-normal" translate>
login.expired-password-reset-message
</mat-card-subtitle>
</mat-card-header>
<mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async">
<div class="h-3 px-4"><div tb-toast></div></div>
<mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading">
</mat-progress-bar>
<span style="height: 4px;" *ngIf="!(isLoading$ | async)"></span>
<mat-card-content>
<span style="height: 4px;" *ngIf="!isLoading"></span>
<mat-card-content class="!p-6">
<form [formGroup]="resetPassword" (ngSubmit)="onResetPassword()">
<fieldset [disabled]="isLoading$ | async">
<div tb-toast class="layout-padding flex flex-col">
<span style="height: 50px;"></span>
<mat-form-field class="mat-block tb-appearance-transparent">
<mat-label translate>login.new-password</mat-label>
<input matInput
type="password"
autofocus
cdkOverlayOrigin
#passwordTrigger="cdkOverlayOrigin"
(focus)="passwordTooltip.onFocus()"
(blur)="passwordTooltip.onBlur()"
formControlName="newPassword"/>
<mat-icon class="material-icons" matPrefix>lock</mat-icon>
<tb-toggle-password matSuffix></tb-toggle-password>
<mat-error *ngIf="this.resetPassword.get('newPassword').errors">
{{ 'security.password-requirement.password-not-meet-requirements' | translate }}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block tb-appearance-transparent">
<mat-label translate>login.new-password-again</mat-label>
<input matInput type="password" formControlName="newPassword2"/>
<mat-icon class="material-icons" matPrefix>lock</mat-icon>
<tb-toggle-password matSuffix></tb-toggle-password>
<mat-error *ngIf="resetPassword.get('newPassword2').hasError('passwordsNotMatch')">
{{ 'security.password-requirement.new-passwords-not-match' | translate }}
</mat-error>
</mat-form-field>
<div class="flex flex-col items-center justify-start gap-4 gt-sm:flex-row gt-sm:items-start gt-sm:justify-center">
<button mat-raised-button color="accent" type="submit" [disabled]="(isLoading$ | async)">
{{ 'login.reset-password' | translate }}
</button>
<button mat-button type="button" [disabled]="(isLoading$ | async)"
routerLink="/login">
{{ 'action.cancel' | translate }}
</button>
</div>
</div>
</fieldset>
<mat-form-field class="mat-block" appearance="outline">
<mat-label translate>login.new-password</mat-label>
<input matInput
type="password"
autofocus
cdkOverlayOrigin
autocomplete="new-password"
#passwordTrigger="cdkOverlayOrigin"
(focus)="passwordTooltip.onFocus()"
(blur)="passwordTooltip.onBlur()"
formControlName="newPassword"/>
<mat-icon class="material-icons" matPrefix>lock</mat-icon>
<tb-toggle-password matSuffix></tb-toggle-password>
<mat-error *ngIf="this.resetPassword.get('newPassword').errors">
{{ 'security.password-requirement.password-not-meet-requirements' | translate }}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block" appearance="outline" style="margin-bottom: 18px">
<mat-label translate>login.new-password-again</mat-label>
<input matInput type="password" formControlName="newPassword2"/>
<mat-icon class="material-icons" matPrefix>lock</mat-icon>
<tb-toggle-password matSuffix></tb-toggle-password>
<mat-error *ngIf="resetPassword.get('newPassword2').hasError('passwordsNotMatch')">
{{ 'security.password-requirement.new-passwords-not-match' | translate }}
</mat-error>
</mat-form-field>
<div class="flex flex-col items-center justify-start gap-4 gt-sm:flex-row gt-sm:items-start gt-sm:justify-center">
<button mat-flat-button color="accent" type="submit" [disabled]="isLoading">
{{ 'login.reset-password' | translate }}
</button>
<button mat-button type="button" [disabled]="isLoading" routerLink="/login">
{{ 'action.cancel' | translate }}
</button>
</div>
</form>
</mat-card-content>
</mat-card>

6
ui-ngx/src/app/modules/login/pages/login/reset-password.component.scss

@ -20,15 +20,11 @@
flex: 1 1 0;
.tb-reset-password-content {
background-color: #eee;
--mdc-elevated-card-container-elevation: none;
.tb-reset-password-card {
@media #{$mat-gt-sm} {
width: 450px !important;
}
}
.tb-card-title{
padding-top: 0;
padding-bottom: 0;
}
}
}

17
ui-ngx/src/app/modules/login/pages/login/reset-password.component.ts

@ -16,7 +16,6 @@
import { Component } from '@angular/core';
import { AuthService } from '@core/auth/auth.service';
import { PageComponent } from '@shared/components/page.component';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
import { UserPasswordPolicy } from '@shared/models/settings.models';
@ -27,9 +26,10 @@ import { passwordsMatchValidator, passwordStrengthValidator } from '@shared/mode
templateUrl: './reset-password.component.html',
styleUrls: ['./reset-password.component.scss']
})
export class ResetPasswordComponent extends PageComponent {
export class ResetPasswordComponent {
isExpiredPassword: boolean;
isLoading = false;
resetPassword: FormGroup;
passwordPolicy: UserPasswordPolicy;
@ -40,7 +40,6 @@ export class ResetPasswordComponent extends PageComponent {
private router: Router,
private authService: AuthService,
private fb: FormBuilder) {
super();
this.resetToken = this.route.snapshot.queryParams['resetToken'] || '';
this.passwordPolicy = this.route.snapshot.data['passwordPolicy'];
@ -62,13 +61,13 @@ export class ResetPasswordComponent extends PageComponent {
onResetPassword() {
if (this.resetPassword.invalid) {
this.resetPassword.markAllAsTouched();
this.resetPassword.markAllAsTouched();
} else {
this.authService.resetPassword(
this.resetToken,
this.resetPassword.get('newPassword').value).subscribe(
() => this.router.navigateByUrl('login')
);
this.isLoading = true;
this.authService.resetPassword(this.resetToken, this.resetPassword.get('newPassword').value).subscribe({
next: () => this.router.navigateByUrl('login'),
error: () => {this.isLoading = false;}
});
}
}
}

4
ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.html

@ -41,7 +41,7 @@
<div tb-toast class="flex flex-col">
<p class="mat-body">{{ providerDescription }}</p>
<div class="code-block flex flex-row gap-2">
<mat-form-field class="mat-block tb-appearance-transparent flex-1">
<mat-form-field class="mat-block flex-1" appearance="outline">
<input matInput formControlName="verificationCode"
required [maxlength]="maxLengthInput" type="text"
[attr.inputmode]="inputMode" [pattern]="pattern"
@ -62,7 +62,7 @@
</mat-form-field>
</div>
<span style="height: 50px;"></span>
<button mat-raised-button
<button mat-flat-button
color="accent"
[disabled]="(isLoading$ | async) || verificationForm.invalid"
type="submit">

1
ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.scss

@ -21,6 +21,7 @@
.tb-two-factor-auth-login-content {
background-color: #eee;
--mdc-elevated-card-container-elevation: none;
.tb-two-factor-auth-login-card {
padding: 48px 48px 48px 16px;

1
ui-ngx/src/assets/locale/locale.constant-ar_AE.json

@ -3882,7 +3882,6 @@
"two-factor-authentication": "المصادقة الثنائية العاملة",
"passwords-mismatch-error": "يجب أن تكون كلمات المرور المدخلة متطابقة!",
"password-again": "إعادة كلمة المرور",
"sign-in": "الرجاء تسجيل الدخول",
"username": "اسم المستخدم (البريد الإلكتروني)",
"remember-me": "تذكرني",
"forgot-password": "نسيت كلمة المرور؟",

1
ui-ngx/src/assets/locale/locale.constant-ca_ES.json

@ -3233,7 +3233,6 @@
"create-password": "Crear contrasenya",
"passwords-mismatch-error": "¡Les contraseñas introduïdes han de ser iguals!",
"password-again": "Repetiu la contrasenya de nou",
"sign-in": "Per favor, inicieu sessió",
"username": "Nom d'usuari (correu electrònic)",
"remember-me": "Recordar-me",
"forgot-password": "Ha oblidat la contrasenya?",

1
ui-ngx/src/assets/locale/locale.constant-cs_CZ.json

@ -2151,7 +2151,6 @@
"create-password": "Vytvořit heslo",
"passwords-mismatch-error": "Zadaná hesla se musí shodovat!",
"password-again": "Heslo znovu",
"sign-in": "Prosím zaregistrujte se",
"username": "Uživatelské jméno (email)",
"remember-me": "Zapamatovat si mě",
"forgot-password": "Zapomněli jste heslo?",

1
ui-ngx/src/assets/locale/locale.constant-de_DE.json

@ -3768,7 +3768,6 @@
"two-factor-authentication": "Zwei-Faktor-Authentifizierung",
"passwords-mismatch-error": "Die eingegebenen Passwörter müssen übereinstimmen!",
"password-again": "Passwort wiederholen",
"sign-in": "Bitte anmelden",
"username": "Benutzername (E-Mail)",
"remember-me": "Angemeldet bleiben",
"forgot-password": "Passwort vergessen?",

1
ui-ngx/src/assets/locale/locale.constant-el_GR.json

@ -3768,7 +3768,6 @@
"two-factor-authentication": "Έλεγχος ταυτότητας δύο παραγόντων",
"passwords-mismatch-error": "Οι κωδικοί πρέπει να είναι ίδιοι!",
"password-again": "Επαλήθευση κωδικού",
"sign-in": "Παρακαλώ συνδεθείτε",
"username": "Όνομα χρήστη (email)",
"remember-me": "Να με θυμάσαι",
"forgot-password": "Ξεχάσατε τον κωδικό;",

15
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -4259,24 +4259,25 @@
},
"login": {
"login": "Login",
"request-password-reset": "Request Password Reset",
"reset-password": "Reset Password",
"create-password": "Create Password",
"request-password-reset": "Request password reset",
"reset-password": "Reset password",
"create-password": "Create password",
"two-factor-authentication": "Two factor authentication",
"passwords-mismatch-error": "Entered passwords must be same!",
"password-again": "Password again",
"sign-in": "Please sign in",
"sign-in": "Sign in",
"username": "Username (email)",
"remember-me": "Remember me",
"forgot-password": "Forgot Password?",
"password-reset": "Password Reset",
"forgot-password": "Forgot your password?",
"password-reset": "Password reset",
"expired-password-reset-message": "Your password has expired! \nPlease enter a new password.",
"new-password": "New password",
"new-password-again": "Confirm new password",
"password-link-sent-message": "Reset link has been sent",
"email": "Email",
"invalid-email-format": "Invalid email format.",
"login-with": "Login with {{name}}",
"sign-in-with": "Sign in with {{name}}",
"sign-in-to-your-account": "Sign in to your account",
"or": "or",
"error": "Login error",
"verify-your-identity": "Verify your identity",

1
ui-ngx/src/assets/locale/locale.constant-es_ES.json

@ -3768,7 +3768,6 @@
"two-factor-authentication": "Autenticación de dos factores",
"passwords-mismatch-error": "¡Las contraseñas ingresadas deben coincidir!",
"password-again": "Repetir contraseña",
"sign-in": "Por favor inicia sesión",
"username": "Nombre de usuario (correo electrónico)",
"remember-me": "Recordarme",
"forgot-password": "¿Olvidaste tu contraseña?",

1
ui-ngx/src/assets/locale/locale.constant-fa_IR.json

@ -1131,7 +1131,6 @@
"create-password": "ايجاد رمز عبور",
"passwords-mismatch-error": "!رمزهاي عبور وارد شده بايد مشابه باشند",
"password-again": "رمز عبور دوباره",
"sign-in": "لطفا وارد شويد",
"username": "(نام کاربري (پست الکترونيک",
"remember-me": "مرا به خاطر داشته باش",
"forgot-password": "رمز عبور را فراموش کرده ايد؟",

1
ui-ngx/src/assets/locale/locale.constant-fr_FR.json

@ -3768,7 +3768,6 @@
"two-factor-authentication": "Authentification à deux facteurs",
"passwords-mismatch-error": "Les mots de passe saisis doivent être identiques !",
"password-again": "Ressaisir le mot de passe",
"sign-in": "Veuillez vous connecter",
"username": "Nom d'utilisateur (email)",
"remember-me": "Se souvenir de moi",
"forgot-password": "Mot de passe oublié ?",

1
ui-ngx/src/assets/locale/locale.constant-it_IT.json

@ -3652,7 +3652,6 @@
"two-factor-authentication": "Autenticazione a due fattori",
"passwords-mismatch-error": "Le password inserite devono coincidere!",
"password-again": "Ripeti password",
"sign-in": "Accedi",
"username": "Nome utente (email)",
"remember-me": "Ricordami",
"forgot-password": "Password dimenticata?",

1
ui-ngx/src/assets/locale/locale.constant-ja_JP.json

@ -1016,7 +1016,6 @@
"create-password": "パスワードの作成",
"passwords-mismatch-error": "入力されたパスワードは同じでなければなりません!",
"password-again": "パスワードをもう一度",
"sign-in": "サインインしてください",
"username": "ユーザー名(電子メール)",
"remember-me": "ログイン情報を記憶",
"forgot-password": "パスワードをお忘れですか?",

1
ui-ngx/src/assets/locale/locale.constant-ka_GE.json

@ -1250,7 +1250,6 @@
"create-password": "პაროლის შექმნა",
"passwords-mismatch-error": "პაროლები არ ემთხვევა",
"password-again": "შეიყვანეთ პაროლი თავიდა",
"sign-in": "სისტემაში შესვლა",
"username": "მომხმარებლის სახელი",
"remember-me": "დამახსოვრება",
"forgot-password": " დაგავიწყდა პაროლი?",

1
ui-ngx/src/assets/locale/locale.constant-ko_KR.json

@ -1708,7 +1708,6 @@
"create-password": "비밀번호 생성",
"passwords-mismatch-error": "입력된 비밀번호는 같아야 합니다!",
"password-again": "비밀번호 확인",
"sign-in": "로그인",
"username": "사용자명 (이메일)",
"remember-me": "아이디 저장",
"forgot-password": "비밀번호찾기",

1
ui-ngx/src/assets/locale/locale.constant-lt_LT.json

@ -3767,7 +3767,6 @@
"two-factor-authentication": "Dviejų veiksnių autentifikavimas",
"passwords-mismatch-error": "Įvesti slaptažodžiai turi sutapti!",
"password-again": "Pakartokite slaptažodį",
"sign-in": "Prisijungti",
"username": "Vartotojo vardas (el. paštas)",
"remember-me": "Prisiminti mane",
"forgot-password": "Pamiršote slaptažodį?",

1
ui-ngx/src/assets/locale/locale.constant-lv_LV.json

@ -1178,7 +1178,6 @@
"create-password": "Radīt paroli",
"passwords-mismatch-error": "Ievadītajai parolei ir jāsakrīt!",
"password-again": "Atkārtot paroli",
"sign-in": "Lūdzu pierakstīties",
"username": "Lietotājvārds (email)",
"remember-me": "Atcerēties mani",
"forgot-password": "Aizmirsu paroli?",

1
ui-ngx/src/assets/locale/locale.constant-nl_BE.json

@ -3847,7 +3847,6 @@
"two-factor-authentication": "Twee-factor-authenticatie",
"passwords-mismatch-error": "Ingevoerde wachtwoorden moeten hetzelfde zijn.",
"password-again": "Wachtwoord opnieuw ingeven",
"sign-in": "Gelieve in te loggen",
"username": "Gebruikersnaam (e-mail)",
"remember-me": "Onthoud mij",
"forgot-password": "Wachtwoord vergeten?",

1
ui-ngx/src/assets/locale/locale.constant-nl_NL.json

@ -3652,7 +3652,6 @@
"two-factor-authentication": "Twee-factor authenticatie",
"passwords-mismatch-error": "Ingevoerde wachtwoorden moeten gelijk zijn!",
"password-again": "Wachtwoord opnieuw",
"sign-in": "Meld u aan",
"username": "Gebruikersnaam (e-mail)",
"remember-me": "Onthoud mij",
"forgot-password": "Wachtwoord vergeten?",

1
ui-ngx/src/assets/locale/locale.constant-no_NO.json

@ -3652,7 +3652,6 @@
"two-factor-authentication": "Tofaktorautentisering",
"passwords-mismatch-error": "Passordene må være like!",
"password-again": "Gjenta passord",
"sign-in": "Vennligst logg inn",
"username": "Brukernavn (epost)",
"remember-me": "Husk meg",
"forgot-password": "Glemt passord?",

1
ui-ngx/src/assets/locale/locale.constant-pl_PL.json

@ -3802,7 +3802,6 @@
"two-factor-authentication": "Uwierzytelnianie dwuskładnikowe",
"passwords-mismatch-error": "Wpisane hasła muszą być takie same!",
"password-again": "Hasło ponownie",
"sign-in": "proszę, zaloguj się",
"username": "Nazwa użytkownika (e-mail)",
"remember-me": "Zapamiętaj mnie",
"forgot-password": "Zapomniałeś hasła?",

1
ui-ngx/src/assets/locale/locale.constant-pt_BR.json

@ -1383,7 +1383,6 @@
"create-password": "Criar senha",
"passwords-mismatch-error": "As senhas inseridas devem ser idênticas!",
"password-again": "Repetir senha",
"sign-in": "Inscreva-se",
"username": "Nome de usuário (e-mail)",
"remember-me": "Lembrar-me",
"forgot-password": "Esqueceu a senha?",

1
ui-ngx/src/assets/locale/locale.constant-ro_RO.json

@ -1236,7 +1236,6 @@
"create-password": "Creează Parolă",
"passwords-mismatch-error": "Parola reintrodusă trebuie să fie identică!",
"password-again": "Rescrie Parola",
"sign-in": "Intră în Cont",
"username": "Nume Utilizator (Adresa De eMail)",
"remember-me": "Ține-mă minte!",
"forgot-password": "Ai Uitat Parola?",

1
ui-ngx/src/assets/locale/locale.constant-sl_SI.json

@ -1708,7 +1708,6 @@
"create-password": "Ustvari geslo",
"passwords-mismatch-error": "Vnesena gesla morajo biti enaka!",
"password-again": "Ponovi geslo",
"sign-in": "Prosimo, prijavite se",
"username": "Uporabniško ime (e-pošta)",
"remember-me": "Zapomni si me",
"forgot-password": "Ste pozabili geslo?",

1
ui-ngx/src/assets/locale/locale.constant-tr_TR.json

@ -3768,7 +3768,6 @@
"two-factor-authentication": "İki adımlı doğrulama",
"passwords-mismatch-error": "Girilen şifreler aynı olmalıdır!",
"password-again": "Şifre tekrar",
"sign-in": "Lütfen giriş yapın",
"username": "Kullanıcı adı (e-posta)",
"remember-me": "Beni hatırla",
"forgot-password": "Şifrenizi mi unuttunuz?",

1
ui-ngx/src/assets/locale/locale.constant-uk_UA.json

@ -1793,7 +1793,6 @@
"create-password": "Створити пароль",
"passwords-mismatch-error": "Введені паролі повинні бути однаковими!",
"password-again": "Введіть пароль ще раз",
"sign-in": "Будь ласка, увійдіть в систему",
"username": "Ім'я користувача (ел. пошта)",
"remember-me": "Запам'ятати мене",
"forgot-password": "Забули пароль?",

1
ui-ngx/src/assets/locale/locale.constant-zh_CN.json

@ -3473,7 +3473,6 @@
"two-factor-authentication": "双因素认证",
"passwords-mismatch-error": "输入的密码必须相同!",
"password-again": "再次输入密码",
"sign-in": "登录 ",
"username": "用户名(电子邮件)",
"remember-me": "记住我",
"forgot-password": "忘记密码?",

1
ui-ngx/src/assets/locale/locale.constant-zh_TW.json

@ -2404,7 +2404,6 @@
"two-factor-authentication": "雙因素身份驗證",
"passwords-mismatch-error": "輸入的密碼必須相同!",
"password-again": "再次輸入密碼",
"sign-in": "登入 ",
"username": "用戶名(電子郵件)",
"remember-me": "記住我",
"forgot-password": "忘記密碼?",

33
ui-ngx/src/styles.scss

@ -636,6 +636,39 @@ pre.tb-highlight {
}
}
.tb-autofilled:has(input:-webkit-autofill){
--mdc-outlined-text-field-label-text-color: var(--mdc-outlined-text-field-input-text-color);
--mdc-outlined-text-field-hover-label-text-color: var(--mdc-outlined-text-field-input-text-color);
.mat-mdc-form-field-flex {
&:before {
display: block;
height: auto;
content: "";
position: absolute;
inset: 0;
background: #7986cb;
opacity: 0.5;
pointer-events: none;
border-radius: 4px;
}
}
.mdc-floating-label {
--mat-mdc-form-field-label-transform: translateY(calc(calc(6.7px + var(--mat-form-field-container-height) / 2) * -1)) scale(var(--mat-mdc-form-field-floating-label-scale, 0.75));
}
.mat-mdc-form-field-infix {
input {
&:-webkit-autofill,
&:-webkit-autofill:hover,
&:-webkit-autofill:focus,
&:-webkit-autofill:active {
-webkit-background-clip: text !important;
-webkit-text-fill-color: var(--mdc-outlined-text-field-input-text-color);
}
}
}
}
// Material table
.mat-toolbar.mat-primary {

Loading…
Cancel
Save