feat: add login page

This commit is contained in:
Polianin Nikita 2024-08-04 23:14:45 +03:00
parent b764f2a77b
commit f4b25f428d
4 changed files with 200 additions and 1 deletions

View File

@ -8,6 +8,7 @@ import {ScheduleComponent as SetupScheduleComponent} from "@page/setup/schedule/
import {SetupComponent} from "@page/setup/setup.component";
import {CreateAdminComponent} from "@page/setup/create-admin/create-admin.component";
import {SummaryComponent} from "@page/setup/summary/summary.component";
import {LoginComponent} from "@page/login/login.component";
export const routes: Routes = [
{path: '', title: 'Расписание', pathMatch: 'full', component: ScheduleComponent},
@ -22,7 +23,8 @@ export const routes: Routes = [
{path: 'summary', component: SummaryComponent},
{path: '', redirectTo: 'welcome', pathMatch: 'full'}
]
}
},
{path: 'login', title: 'Вход', component: LoginComponent},
/*{path: 'not-found', title: '404 страница не найдена'},
{path: '**', redirectTo: '/not-found'}*/
];

View File

@ -0,0 +1,26 @@
.formLogin {
display: flex;
padding: 20px;
justify-content: center;
align-items: center;
min-height: 40vh;
height: auto;
}
.formLogin mat-card {
padding: 25px;
}
.formLogin p {
text-align: center
}
.formLogin form {
display: flex;
flex-direction:column;
}
.formLoginButton {
display: flex;
justify-content: flex-end;
}

View File

@ -0,0 +1,76 @@
<mat-sidenav-container class="formLogin">
<mat-card>
<p class="mat-h3">
Вход в систему
</p>
<form [formGroup]="loginForm">
<mat-form-field color="accent">
<mat-label>Имя пользователя/email</mat-label>
<input matInput
formControlName="user"
matTooltip='Укажите имя пользователя используя латинские буквы и цифры без пробелов или email'
required
focusNext="passwordNextFocus">
@if (loginForm.get('user')?.hasError('required')) {
<mat-error>
Имя пользователя или email является <i>обязательным</i>
</mat-error>
}
@if (loginForm.get('user')?.hasError('minlength')) {
<mat-error>
Количество символов должно быть не менее 4
</mat-error>
}
</mat-form-field>
<mat-form-field color="accent" style="margin-bottom: 20px">
<mat-label>Пароль</mat-label>
<input matInput
matTooltip="Укажите пароль"
formControlName="password"
required
[type]="hidePass ? 'password' : 'text'"
id="passwordNextFocus"
focusNext="loginNextFocus">
<button mat-icon-button matSuffix (click)="togglePassword($event)" [attr.aria-label]="'Hide password'"
[attr.aria-pressed]="hidePass">
<mat-icon>{{ hidePass ? 'visibility_off' : 'visibility' }}</mat-icon>
</button>
@if (loginForm.get('password')?.hasError('required')) {
<mat-error>
Пароль является <i>обязательным</i>
</mat-error>
}
@if (loginForm.get('password')?.hasError('minlength')) {
<mat-error>
Пароль должен быть не менее 8 символов
</mat-error>
}
</mat-form-field>
</form>
<mat-error>
{{errorText}}
</mat-error>
<div class="formLoginButton">
@if (loaderActive) {
<app-data-spinner [scale]="40"/>
} @else {
<button mat-flat-button color="accent"
[disabled]="loginButtonIsDisable"
(click)="login()"
id="loginNextFocus">
Войти
</button>
}
</div>
</mat-card>
</mat-sidenav-container>

View File

@ -0,0 +1,95 @@
import {Component} from '@angular/core';
import {MatSidenavContainer} from "@angular/material/sidenav";
import {MatFormFieldModule} from "@angular/material/form-field";
import {MatInput} from "@angular/material/input";
import {MatTooltip} from "@angular/material/tooltip";
import {MatIcon} from "@angular/material/icon";
import {MatButton, MatIconButton} from "@angular/material/button";
import {MatCard} from "@angular/material/card";
import {FormBuilder, FormGroup, ReactiveFormsModule, Validators} from "@angular/forms";
import {FocusNextDirective} from "@/directives/focus-next.directive";
import {DataSpinnerComponent} from "@component/common/data-spinner/data-spinner.component";
import AuthApiService from "@api/v1/authApiService";
import {Router} from "@angular/router";
import {catchError, of} from "rxjs";
@Component({
selector: 'app-login',
standalone: true,
imports: [
MatSidenavContainer,
MatFormFieldModule,
MatInput,
MatTooltip,
MatIcon,
MatIconButton,
MatButton,
MatCard,
ReactiveFormsModule,
FocusNextDirective,
DataSpinnerComponent
],
templateUrl: './login.component.html',
styleUrl: './login.component.css',
providers: [AuthApiService]
})
export class LoginComponent {
protected loginForm!: FormGroup;
protected hidePass: boolean = true;
protected loaderActive: boolean = false;
protected loginButtonIsDisable: boolean = true;
protected errorText: string = '';
constructor(private formBuilder: FormBuilder, private auth: AuthApiService, private route: Router) {
this.auth.getRole()
.subscribe(data => {
if (data != null)
route.navigate(['admin']).then();
});
this.loginForm = this.formBuilder.group({
user: ['',],
password: ['',]
}
);
this.loginForm.get('password')?.setValidators([
Validators.required,
Validators.minLength(8)
]);
this.loginForm.get('user')?.setValidators([
Validators.required,
Validators.minLength(4)
]);
this.loginForm.valueChanges.subscribe(() => {
this.loginButtonIsDisable = !this.loginForm.valid;
});
}
protected togglePassword(event: MouseEvent) {
this.hidePass = !this.hidePass;
event.stopPropagation();
}
protected login() {
this.loaderActive = true;
this.auth.login({
username: this.loginForm.get('user')?.value,
password: this.loginForm.get('password')?.value
})
.pipe(catchError(error => {
this.loaderActive = false;
this.errorText = error.error;
this.loginButtonIsDisable = true;
throw error;
}))
.subscribe(_ => {
this.loaderActive = false;
this.errorText = '';
this.route.navigate(['admin']).then();
});
}
}