diff --git a/src/main/ts/.editorconfig b/src/main/ts/.editorconfig index 4389119..e89330a 100644 --- a/src/main/ts/.editorconfig +++ b/src/main/ts/.editorconfig @@ -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 diff --git a/src/main/ts/package.json b/src/main/ts/package.json index 81de99e..6905e4e 100644 --- a/src/main/ts/package.json +++ b/src/main/ts/package.json @@ -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", diff --git a/src/main/ts/proxy.conf.json b/src/main/ts/proxy.conf.json new file mode 100644 index 0000000..7d4976b --- /dev/null +++ b/src/main/ts/proxy.conf.json @@ -0,0 +1,6 @@ +{ + "/api": { + "target": "http://localhost:8080", + "secure": false + } +} diff --git a/src/main/ts/src/app/app-routing.module.ts b/src/main/ts/src/app/app-routing.module.ts index d425c6f..f49aa1a 100644 --- a/src/main/ts/src/app/app-routing.module.ts +++ b/src/main/ts/src/app/app-routing.module.ts @@ -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)], diff --git a/src/main/ts/src/app/app.module.ts b/src/main/ts/src/app/app.module.ts index 74af512..d2dd112 100644 --- a/src/main/ts/src/app/app.module.ts +++ b/src/main/ts/src/app/app.module.ts @@ -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 ] diff --git a/src/main/ts/src/app/core/entities.ts b/src/main/ts/src/app/core/entities.ts new file mode 100644 index 0000000..9712033 --- /dev/null +++ b/src/main/ts/src/app/core/entities.ts @@ -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 + ) {} + + public static new(): Application { + return new Application('', '', []); + } +} diff --git a/src/main/ts/src/app/core/interceptors/service-unavailable.interceptor.ts b/src/main/ts/src/app/core/interceptors/service-unavailable.interceptor.ts new file mode 100644 index 0000000..4e23192 --- /dev/null +++ b/src/main/ts/src/app/core/interceptors/service-unavailable.interceptor.ts @@ -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, next: HttpHandler): Observable> { + 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); + })); + } +} diff --git a/src/main/ts/src/app/core/services/user.service.spec.ts b/src/main/ts/src/app/core/services/user.service.spec.ts new file mode 100644 index 0000000..79eb548 --- /dev/null +++ b/src/main/ts/src/app/core/services/user.service.spec.ts @@ -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(); + })); +}); diff --git a/src/main/ts/src/app/core/services/user.service.ts b/src/main/ts/src/app/core/services/user.service.ts new file mode 100644 index 0000000..8afa7f1 --- /dev/null +++ b/src/main/ts/src/app/core/services/user.service.ts @@ -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 { + return this.httpClient.post(`/api/users/login`, user); + } + + disconnection(): Observable { + return this.httpClient.get(`/api/users/disconnection`); + } +} diff --git a/src/main/ts/src/app/header/header.component.html b/src/main/ts/src/app/header/header.component.html index a43c536..7b880c5 100644 --- a/src/main/ts/src/app/header/header.component.html +++ b/src/main/ts/src/app/header/header.component.html @@ -11,9 +11,10 @@ - + diff --git a/src/main/ts/src/app/header/header.component.ts b/src/main/ts/src/app/header/header.component.ts index d0d0ebc..620e720 100644 --- a/src/main/ts/src/app/header/header.component.ts +++ b/src/main/ts/src/app/header/header.component.ts @@ -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() { } diff --git a/src/main/ts/src/app/login/login.component.html b/src/main/ts/src/app/login/login.component.html new file mode 100644 index 0000000..4e0610e --- /dev/null +++ b/src/main/ts/src/app/login/login.component.html @@ -0,0 +1,59 @@ + diff --git a/src/main/ts/src/app/login/login.component.scss b/src/main/ts/src/app/login/login.component.scss new file mode 100644 index 0000000..be3aabd --- /dev/null +++ b/src/main/ts/src/app/login/login.component.scss @@ -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; + } + } +} diff --git a/src/main/ts/src/app/login/login.component.spec.ts b/src/main/ts/src/app/login/login.component.spec.ts new file mode 100644 index 0000000..556444c --- /dev/null +++ b/src/main/ts/src/app/login/login.component.spec.ts @@ -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; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ LoginComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(LoginComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/main/ts/src/app/login/login.component.ts b/src/main/ts/src/app/login/login.component.ts new file mode 100644 index 0000000..f3d865c --- /dev/null +++ b/src/main/ts/src/app/login/login.component.ts @@ -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(); + } +} diff --git a/src/main/ts/src/app/service-unavailable/service-unavailable.component.html b/src/main/ts/src/app/service-unavailable/service-unavailable.component.html new file mode 100644 index 0000000..44277cf --- /dev/null +++ b/src/main/ts/src/app/service-unavailable/service-unavailable.component.html @@ -0,0 +1,18 @@ +
+ + + + + + + +

Service indisponible

+
+ + + Le serveur est actuellement indisponible.
+ Veuillez contacter l'administrateur. +
+
+
+
diff --git a/src/main/ts/src/app/service-unavailable/service-unavailable.component.scss b/src/main/ts/src/app/service-unavailable/service-unavailable.component.scss new file mode 100644 index 0000000..8bfdb4e --- /dev/null +++ b/src/main/ts/src/app/service-unavailable/service-unavailable.component.scss @@ -0,0 +1,4 @@ +#content { + width: 450px; + margin: auto; +} diff --git a/src/main/ts/src/app/service-unavailable/service-unavailable.component.spec.ts b/src/main/ts/src/app/service-unavailable/service-unavailable.component.spec.ts new file mode 100644 index 0000000..af01ef0 --- /dev/null +++ b/src/main/ts/src/app/service-unavailable/service-unavailable.component.spec.ts @@ -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; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ServiceUnavailableComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ServiceUnavailableComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/main/ts/src/app/service-unavailable/service-unavailable.component.ts b/src/main/ts/src/app/service-unavailable/service-unavailable.component.ts new file mode 100644 index 0000000..ee0efba --- /dev/null +++ b/src/main/ts/src/app/service-unavailable/service-unavailable.component.ts @@ -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() {} +} diff --git a/src/main/ts/src/assets/images/icon-128.png b/src/main/ts/src/assets/images/icon-128.png new file mode 100644 index 0000000..97ef7f9 Binary files /dev/null and b/src/main/ts/src/assets/images/icon-128.png differ diff --git a/src/main/ts/src/assets/images/icon-64.png b/src/main/ts/src/assets/images/icon-64.png new file mode 100644 index 0000000..da14153 Binary files /dev/null and b/src/main/ts/src/assets/images/icon-64.png differ diff --git a/src/main/ts/src/assets/images/service-unavailable.jpg b/src/main/ts/src/assets/images/service-unavailable.jpg new file mode 100644 index 0000000..d58efad Binary files /dev/null and b/src/main/ts/src/assets/images/service-unavailable.jpg differ