Add login component.
This commit is contained in:
@@ -3,7 +3,7 @@ root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = tab
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"version": "0.0.0",
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
"start": "ng serve",
|
||||
"start": "ng serve --proxy-config proxy.conf.json",
|
||||
"build": "ng build",
|
||||
"test": "ng test",
|
||||
"lint": "ng lint",
|
||||
|
||||
6
src/main/ts/proxy.conf.json
Normal file
6
src/main/ts/proxy.conf.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"/api": {
|
||||
"target": "http://localhost:8080",
|
||||
"secure": false
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,12 @@
|
||||
import { AppComponent } from './app.component';
|
||||
import { ServiceUnavailableComponent } from './service-unavailable/service-unavailable.component';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { Routes, RouterModule } from '@angular/router';
|
||||
|
||||
const routes: Routes = [];
|
||||
const routes: Routes = [
|
||||
{ path: 'serviceUnavailable', component: ServiceUnavailableComponent },
|
||||
{ path: '', component: AppComponent }
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forRoot(routes)],
|
||||
|
||||
@@ -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 { NgModule } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
import { AppComponent } from './app.component';
|
||||
@@ -9,21 +13,34 @@ import { HeaderComponent } from './header/header.component';
|
||||
import { FooterComponent } from './footer/footer.component';
|
||||
import { NotificationElement } from './core/notifications/notification-element/notification-element.component';
|
||||
import { NotificationsComponent } from './core/notifications/notifications.component';
|
||||
import { LoginComponent } from './login/login.component';
|
||||
import { ServiceUnavailableComponent } from './service-unavailable/service-unavailable.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppComponent,
|
||||
FooterComponent,
|
||||
HeaderComponent,
|
||||
LoginComponent,
|
||||
NotificationElement,
|
||||
NotificationsComponent
|
||||
NotificationsComponent,
|
||||
ServiceUnavailableComponent
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
AppRoutingModule,
|
||||
BrowserModule,
|
||||
FormsModule,
|
||||
HttpClientModule,
|
||||
MDBBootstrapModule.forRoot()
|
||||
],
|
||||
providers: [],
|
||||
providers: [
|
||||
UserService,
|
||||
{
|
||||
provide: HTTP_INTERCEPTORS,
|
||||
useClass: ServiceUnavailableInterceptor,
|
||||
multi: true
|
||||
}
|
||||
],
|
||||
bootstrap: [
|
||||
AppComponent
|
||||
]
|
||||
|
||||
46
src/main/ts/src/app/core/entities.ts
Normal file
46
src/main/ts/src/app/core/entities.ts
Normal 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('', '', []);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}));
|
||||
}
|
||||
}
|
||||
16
src/main/ts/src/app/core/services/user.service.spec.ts
Normal file
16
src/main/ts/src/app/core/services/user.service.spec.ts
Normal 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();
|
||||
}));
|
||||
});
|
||||
21
src/main/ts/src/app/core/services/user.service.ts
Normal file
21
src/main/ts/src/app/core/services/user.service.ts
Normal 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`);
|
||||
}
|
||||
}
|
||||
@@ -11,9 +11,10 @@
|
||||
</a>
|
||||
</span>
|
||||
<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>
|
||||
Connexion
|
||||
</button>
|
||||
</span>
|
||||
</header>
|
||||
<app-login #loginForm></app-login>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
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({
|
||||
selector: 'app-header',
|
||||
@@ -7,6 +8,7 @@ import { Component, OnInit } from '@angular/core';
|
||||
styleUrls: ['./header.component.scss']
|
||||
})
|
||||
export class HeaderComponent implements OnInit {
|
||||
@ViewChild('loginForm', {static: true}) loginForm: LoginComponent;
|
||||
|
||||
constructor() { }
|
||||
|
||||
|
||||
59
src/main/ts/src/app/login/login.component.html
Normal file
59
src/main/ts/src/app/login/login.component.html
Normal 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>
|
||||
59
src/main/ts/src/app/login/login.component.scss
Normal file
59
src/main/ts/src/app/login/login.component.scss
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
28
src/main/ts/src/app/login/login.component.spec.ts
Normal file
28
src/main/ts/src/app/login/login.component.spec.ts
Normal 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();
|
||||
});
|
||||
});
|
||||
41
src/main/ts/src/app/login/login.component.ts
Normal file
41
src/main/ts/src/app/login/login.component.ts
Normal 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();
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
@@ -0,0 +1,4 @@
|
||||
#content {
|
||||
width: 450px;
|
||||
margin: auto;
|
||||
}
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
@@ -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() {}
|
||||
}
|
||||
BIN
src/main/ts/src/assets/images/icon-128.png
Normal file
BIN
src/main/ts/src/assets/images/icon-128.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
BIN
src/main/ts/src/assets/images/icon-64.png
Normal file
BIN
src/main/ts/src/assets/images/icon-64.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.0 KiB |
BIN
src/main/ts/src/assets/images/service-unavailable.jpg
Normal file
BIN
src/main/ts/src/assets/images/service-unavailable.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 27 KiB |
Reference in New Issue
Block a user