Add login component.

This commit is contained in:
2019-09-05 23:23:40 +02:00
parent dd76d3f543
commit 6a68a329c2
22 changed files with 394 additions and 8 deletions

View File

@@ -3,7 +3,7 @@ root = true
[*] [*]
charset = utf-8 charset = utf-8
indent_style = tab indent_style = space
indent_size = 2 indent_size = 2
insert_final_newline = true insert_final_newline = true
trim_trailing_whitespace = true trim_trailing_whitespace = true

View File

@@ -3,7 +3,7 @@
"version": "0.0.0", "version": "0.0.0",
"scripts": { "scripts": {
"ng": "ng", "ng": "ng",
"start": "ng serve", "start": "ng serve --proxy-config proxy.conf.json",
"build": "ng build", "build": "ng build",
"test": "ng test", "test": "ng test",
"lint": "ng lint", "lint": "ng lint",

View File

@@ -0,0 +1,6 @@
{
"/api": {
"target": "http://localhost:8080",
"secure": false
}
}

View File

@@ -1,7 +1,12 @@
import { AppComponent } from './app.component';
import { ServiceUnavailableComponent } from './service-unavailable/service-unavailable.component';
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router'; import { Routes, RouterModule } from '@angular/router';
const routes: Routes = []; const routes: Routes = [
{ path: 'serviceUnavailable', component: ServiceUnavailableComponent },
{ path: '', component: AppComponent }
];
@NgModule({ @NgModule({
imports: [RouterModule.forRoot(routes)], imports: [RouterModule.forRoot(routes)],

View File

@@ -1,6 +1,10 @@
import { ServiceUnavailableInterceptor } from './core/interceptors/service-unavailable.interceptor';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { UserService } from './core/services/user.service';
import { BrowserModule } from '@angular/platform-browser'; import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { AppRoutingModule } from './app-routing.module'; import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component'; import { AppComponent } from './app.component';
@@ -9,21 +13,34 @@ import { HeaderComponent } from './header/header.component';
import { FooterComponent } from './footer/footer.component'; import { FooterComponent } from './footer/footer.component';
import { NotificationElement } from './core/notifications/notification-element/notification-element.component'; import { NotificationElement } from './core/notifications/notification-element/notification-element.component';
import { NotificationsComponent } from './core/notifications/notifications.component'; import { NotificationsComponent } from './core/notifications/notifications.component';
import { LoginComponent } from './login/login.component';
import { ServiceUnavailableComponent } from './service-unavailable/service-unavailable.component';
@NgModule({ @NgModule({
declarations: [ declarations: [
AppComponent, AppComponent,
FooterComponent, FooterComponent,
HeaderComponent, HeaderComponent,
LoginComponent,
NotificationElement, NotificationElement,
NotificationsComponent NotificationsComponent,
ServiceUnavailableComponent
], ],
imports: [ imports: [
BrowserModule,
AppRoutingModule, AppRoutingModule,
BrowserModule,
FormsModule,
HttpClientModule,
MDBBootstrapModule.forRoot() MDBBootstrapModule.forRoot()
], ],
providers: [], providers: [
UserService,
{
provide: HTTP_INTERCEPTORS,
useClass: ServiceUnavailableInterceptor,
multi: true
}
],
bootstrap: [ bootstrap: [
AppComponent AppComponent
] ]

View File

@@ -0,0 +1,46 @@
export class User {
constructor(
public id: string,
public name: string,
public email: string
) {}
public static new(): User {
return new User('', '', '');
}
}
export class SignUpDTO {
constructor(
public name: string,
public email: string,
public password: string,
public confirmPassword: string
) {}
public static new(): SignUpDTO {
return new SignUpDTO('', '', '', '');
}
}
export class ConfigurationFile {
constructor(
public id: string
) {}
public static new(): ConfigurationFile {
return new ConfigurationFile('');
}
}
export class Application {
constructor(
public id: string,
public name: string,
public configurationFileList: Array<ConfigurationFile>
) {}
public static new(): Application {
return new Application('', '', []);
}
}

View File

@@ -0,0 +1,25 @@
import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpEvent, HttpHandler, HttpRequest } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { Router } from '@angular/router';
import { catchError } from 'rxjs/operators';
import { NotificationsComponent } from '../notifications/notifications.component';
@Injectable({providedIn: 'root'})
export class ServiceUnavailableInterceptor implements HttpInterceptor {
constructor(
private router: Router
) {}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(req).pipe(catchError(ex => {
if (!!ex && ex.status === 504) {
NotificationsComponent.error('Le serveur est actuellement indisponible. Veuillez contacter l\'administrateur.');
this.router.navigate(['/serviceUnavailable']);
}
return throwError(ex);
}));
}
}

View File

@@ -0,0 +1,16 @@
/* tslint:disable:no-unused-variable */
import { TestBed, async, inject } from '@angular/core/testing';
import { UserService } from './user.service';
describe('Service: User', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [UserService]
});
});
it('should ...', inject([UserService], (service: UserService) => {
expect(service).toBeTruthy();
}));
});

View File

@@ -0,0 +1,21 @@
import { Observable } from 'rxjs';
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { User } from '../entities';
@Injectable({
providedIn: 'root'
})
export class UserService {
constructor(
private httpClient: HttpClient
) {}
login(user: User): Observable<void> {
return this.httpClient.post<void>(`/api/users/login`, user);
}
disconnection(): Observable<void> {
return this.httpClient.get<void>(`/api/users/disconnection`);
}
}

View File

@@ -11,9 +11,10 @@
</a> </a>
</span> </span>
<span id="right-area"> <span id="right-area">
<button mdbBtn class="flat rounded-pill" mdbWavesEffect> <button mdbBtn class="flat rounded-pill" (click)="loginForm.show()" mdbWavesEffect>
<i class="fa fa-sign-in-alt"></i> <i class="fa fa-sign-in-alt"></i>
Connexion Connexion
</button> </button>
</span> </span>
</header> </header>
<app-login #loginForm></app-login>

View File

@@ -1,5 +1,6 @@
import { NotificationsComponent } from './../core/notifications/notifications.component'; import { NotificationsComponent } from './../core/notifications/notifications.component';
import { Component, OnInit } from '@angular/core'; import { Component, OnInit, ViewChild } from '@angular/core';
import { LoginComponent } from '../login/login.component';
@Component({ @Component({
selector: 'app-header', selector: 'app-header',
@@ -7,6 +8,7 @@ import { Component, OnInit } from '@angular/core';
styleUrls: ['./header.component.scss'] styleUrls: ['./header.component.scss']
}) })
export class HeaderComponent implements OnInit { export class HeaderComponent implements OnInit {
@ViewChild('loginForm', {static: true}) loginForm: LoginComponent;
constructor() { } constructor() { }

View File

@@ -0,0 +1,59 @@
<div mdbModal
#loginModal="mdbModal"
class="modal fade"
tabindex="-1"
role="dialog"
aria-labelledby="loginModal"
aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<form (ngSubmit)="onSubmit()" ngNativeValidate>
<div class="modal-header">
<button type="button" class="close pull-right" aria-label="Close" (click)="loginModal.hide()">
<span aria-hidden="true">×</span>
</button>
<img src="assets/images/icon-64.png"/>
<h4 class="modal-title w-100">
Connexion
</h4>
</div>
<div class="modal-body">
<!-- Email address -->
<div class="md-form">
<input mdbInputDirective
type="email"
id="email"
name="email"
class="form-control"
#email="ngModel"
[(ngModel)]="model.email"
data-error="Veuillez saisir une adresse email valide"
[validateSuccess]="false"
required >
<label for="email">Adresse email</label>
</div>
<!-- Password -->
<div class="md-form">
<input mdbInputDirective
type="password"
id="password"
name="password"
class="form-control"
#password="ngModel"
[(ngModel)]="model.password"
data-error="Veuillez saisir votre mot de passe"
[validateSuccess]="false"
required >
<label for="password">Mot de passe</label>
</div>
</div>
<div class="modal-footer">
<button mdbBtn type="submit" color="indigo" size="sm" class="rounded-pill" mdbWavesEffect>
Valider
</button>
</div>
</form>
</div>
</div>
</div>

View File

@@ -0,0 +1,59 @@
// #form {
// width: 300px;
// margin: auto;
// margin-top: 50px;
// }
// #logo {
// text-align: center;
// margin-bottom: 15px;
// }
// #title {
// text-align: center;
// font-size: 1.5em;
// }
// #buttons-area {
// button {
// width: 90%;
// margin: auto;
// }
// a {
// margin: auto;
// margin-bottom: 20px;
// }
// }
// #signup {
// text-align: center;
// margin-top: 35px;
// }
.modal-dialog {
margin-top: 50px;
width: 300px;
.modal-content {
.modal-header {
img {
position: absolute;
top: -25px;
left: 0;
right: 0;
margin-left: auto;
margin-right: auto;
width: 64px;
}
.modal-title {
margin-top: 30px;
text-align: center;
font-weight: bolder;
}
}
.modal-footer {
padding: 10px;
}
}
}

View File

@@ -0,0 +1,28 @@
/* tslint:disable:no-unused-variable */
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { DebugElement } from '@angular/core';
import { LoginComponent } from './login.component';
describe('LoginComponent', () => {
let component: LoginComponent;
let fixture: ComponentFixture<LoginComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ LoginComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(LoginComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,41 @@
import { Component, OnInit, ViewChild } from '@angular/core';
import { User } from './../core/entities';
import { ModalDirective } from 'angular-bootstrap-md';
import { UserService } from '../core/services/user.service';
import { NotificationsComponent } from '../core/notifications/notifications.component';
@Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.scss']
})
export class LoginComponent implements OnInit {
model: User = User.new();
@ViewChild('loginModal', {static: true}) loginModal: ModalDirective;
constructor(
private userService: UserService
) {}
ngOnInit() {
}
onSubmit(): void {
this.userService.login(this.model).subscribe(() => {
NotificationsComponent.success('Connexion réussie.');
this.loginModal.hide();
}, ex => {
console.error('Error during login attempt: ', ex);
if (ex.error.status === 400) {
NotificationsComponent.error(`Login ou mot de passe incorrect.`);
} else {
NotificationsComponent.error(`Une erreur technique est survenue :\n${ex.error.message}`);
}
});
}
show(): void {
this.loginModal.show();
}
}

View File

@@ -0,0 +1,18 @@
<div id="content">
<mdb-card>
<!--Card image-->
<mdb-card-img src="assets/images/service-unavailable.jpg" alt="Service unavailable"></mdb-card-img>
<!--Card content-->
<mdb-card-body>
<!--Title-->
<mdb-card-title>
<h4>Service indisponible</h4>
</mdb-card-title>
<!--Text-->
<mdb-card-text>
Le serveur est actuellement indisponible.<br/>
Veuillez contacter l'administrateur.
</mdb-card-text>
</mdb-card-body>
</mdb-card>
</div>

View File

@@ -0,0 +1,4 @@
#content {
width: 450px;
margin: auto;
}

View File

@@ -0,0 +1,28 @@
/* tslint:disable:no-unused-variable */
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { DebugElement } from '@angular/core';
import { ServiceUnavailableComponent } from './service-unavailable.component';
describe('ServiceUnavailableComponent', () => {
let component: ServiceUnavailableComponent;
let fixture: ComponentFixture<ServiceUnavailableComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ ServiceUnavailableComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ServiceUnavailableComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,10 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-service-unavailable',
templateUrl: './service-unavailable.component.html',
styleUrls: ['./service-unavailable.component.scss']
})
export class ServiceUnavailableComponent {
constructor() {}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB