diff --git a/src/main/java/org/cerberus/controllers/ApplicationController.java b/src/main/java/org/cerberus/controllers/ApplicationController.java index 051afbd..6acd6e4 100644 --- a/src/main/java/org/cerberus/controllers/ApplicationController.java +++ b/src/main/java/org/cerberus/controllers/ApplicationController.java @@ -10,6 +10,7 @@ import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; import java.security.Principal; +import java.util.List; import java.util.UUID; import static org.cerberus.core.constant.RoleSecurity.ADMIN; @@ -35,6 +36,13 @@ public class ApplicationController { return service.findByIdOrElseThrow(id); } + @GetMapping + @JsonView({View.ApplicationDTO.class}) + public List findAll(Principal connectedUser) { + securityService.getAdminUser(connectedUser); + return service.findAll(); + } + @PostMapping @JsonView({View.ApplicationDTO.class}) public Application create(@RequestBody Application application, Principal connectedUser) { diff --git a/src/main/java/org/cerberus/entities/persistence/ConfigurationFile.java b/src/main/java/org/cerberus/entities/persistence/ConfigurationFile.java index c1de892..24e3849 100644 --- a/src/main/java/org/cerberus/entities/persistence/ConfigurationFile.java +++ b/src/main/java/org/cerberus/entities/persistence/ConfigurationFile.java @@ -19,6 +19,9 @@ public class ConfigurationFile { @JsonView({View.ConfigurationFileDTO.class}) private String path; + @JsonView({View.ConfigurationFileDTO.class}) + private transient String content; + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "application_id") @JsonView({ConfigurationFile.class}) @@ -45,6 +48,14 @@ public class ConfigurationFile { this.path = path; } + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + public Application getApplication() { return application; } diff --git a/src/main/java/org/cerberus/services/ApplicationService.java b/src/main/java/org/cerberus/services/ApplicationService.java index e402560..090d7a6 100644 --- a/src/main/java/org/cerberus/services/ApplicationService.java +++ b/src/main/java/org/cerberus/services/ApplicationService.java @@ -8,6 +8,7 @@ import org.cerberus.validators.ApplicationValidator; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.List; import java.util.UUID; import static org.cerberus.core.constant.Role.MAINTAINER; @@ -76,4 +77,8 @@ public class ApplicationService extends AbstractService { public int getStatus(UUID applicationId) { return daemonHandlingService.getStatus(findByIdOrElseThrow(applicationId)); } + + public List findAll() { + return repository.findAll(); + } } diff --git a/src/main/java/org/cerberus/services/ConfigurationFileService.java b/src/main/java/org/cerberus/services/ConfigurationFileService.java index 63d7c1f..72bf9c6 100644 --- a/src/main/java/org/cerberus/services/ConfigurationFileService.java +++ b/src/main/java/org/cerberus/services/ConfigurationFileService.java @@ -1,14 +1,20 @@ package org.cerberus.services; import org.cerberus.core.exceptions.BadRequestException; +import org.cerberus.core.exceptions.InternalServerErrorException; import org.cerberus.core.utils.StringUtils; import org.cerberus.entities.persistence.ConfigurationFile; import org.cerberus.repositories.ConfigurationFileRepository; import org.cerberus.validators.ConfigurationFileValidator; import org.springframework.stereotype.Service; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; import java.util.UUID; +import static org.cerberus.core.utils.StringUtils.concat; + @Service public class ConfigurationFileService extends AbstractService { private ApplicationService applicationService; @@ -28,7 +34,16 @@ public class ConfigurationFileService extends AbstractService if(!applicationService.existsById(applicationId)) { throwNotFoundException(); } - return findByIdOrElseThrow(configurationFileId); + + ConfigurationFile configurationFile = findByIdOrElseThrow(configurationFileId); + + try { + configurationFile.setContent(Files.readString(Paths.get(configurationFile.getPath()))); + } catch(IOException ex) { + throw new InternalServerErrorException(concat("Error during file reading for ", configurationFile.getId())); + } + + return configurationFile; } public ConfigurationFile create(UUID applicationId, ConfigurationFile configurationFile) { diff --git a/src/main/ts/src/app/app-routing.module.ts b/src/main/ts/src/app/app-routing.module.ts index 1e73bac..cc4901b 100644 --- a/src/main/ts/src/app/app-routing.module.ts +++ b/src/main/ts/src/app/app-routing.module.ts @@ -1,3 +1,6 @@ +import { UserManagementComponent } from './management/user-management/user-management.component'; +import { AppEditionComponent } from './management/app-management/app-edition/app-edition.component'; +import { AppManagementComponent } from './management/app-management/app-management.component'; import { DisconnectionComponent } from './disconnection/disconnection.component'; import { AppComponent } from './app.component'; import { ServiceUnavailableComponent } from './service-unavailable/service-unavailable.component'; @@ -7,6 +10,9 @@ import { Routes, RouterModule } from '@angular/router'; const routes: Routes = [ { path: 'serviceUnavailable', component: ServiceUnavailableComponent }, { path: 'disconnection', component: DisconnectionComponent }, + { path: 'management/applications', component: AppManagementComponent }, + { path: 'management/applications/:appId', component: AppEditionComponent }, + { path: 'management/users', component: UserManagementComponent }, { path: '', component: AppComponent } ]; diff --git a/src/main/ts/src/app/app.component.scss b/src/main/ts/src/app/app.component.scss index fe38cd3..52d73e2 100644 --- a/src/main/ts/src/app/app.component.scss +++ b/src/main/ts/src/app/app.component.scss @@ -2,5 +2,6 @@ main { &.container { margin-top: 75px; padding: 15px 0; + padding-bottom: 50px; } } diff --git a/src/main/ts/src/app/app.module.ts b/src/main/ts/src/app/app.module.ts index 481304e..6e3f0e3 100644 --- a/src/main/ts/src/app/app.module.ts +++ b/src/main/ts/src/app/app.module.ts @@ -1,3 +1,9 @@ +import { AccordionComponent } from './core/components/accordion/accordion.component'; +import { UserManagementComponent } from './management/user-management/user-management.component'; +import { AppEditionComponent } from './management/app-management/app-edition/app-edition.component'; +import { AppManagementComponent } from './management/app-management/app-management.component'; +import { SideNavElementComponent } from './header/side-nav/side-nav-element/side-nav-element.component'; +import { SideNavComponent } from './header/side-nav/side-nav.component'; import { ServiceUnavailableInterceptor } from './core/interceptors/service-unavailable.interceptor'; import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http'; import { UserService } from './core/services/user.service'; @@ -26,7 +32,13 @@ import { DisconnectionComponent } from './disconnection/disconnection.component' NotificationElement, NotificationsComponent, ServiceUnavailableComponent, - DisconnectionComponent + DisconnectionComponent, + SideNavComponent, + SideNavElementComponent, + AppManagementComponent, + AppEditionComponent, + UserManagementComponent, + AccordionComponent ], imports: [ AppRoutingModule, diff --git a/src/main/ts/src/app/core/components/accordion/accordion.component.html b/src/main/ts/src/app/core/components/accordion/accordion.component.html new file mode 100644 index 0000000..b168dba --- /dev/null +++ b/src/main/ts/src/app/core/components/accordion/accordion.component.html @@ -0,0 +1,6 @@ +
+ +
+ +
+
diff --git a/src/main/ts/src/app/core/components/accordion/accordion.component.scss b/src/main/ts/src/app/core/components/accordion/accordion.component.scss new file mode 100644 index 0000000..90b1757 --- /dev/null +++ b/src/main/ts/src/app/core/components/accordion/accordion.component.scss @@ -0,0 +1,41 @@ +#wrapper { + background-color: #c9dbf3; + + width: 100%; + + #accordion { + background-color: #eee; + border: none; + color: #444; + cursor: pointer; + outline: none; + padding: 18px; + text-align: left; + transition: 0.4s; + width: 100%; + + &.active, &:hover { + background-color: #ccc; + } + + &:after { + content: '\02795'; + font-size: 13px; + color: #777; + float: right; + margin-left: 5px; + } + + &.active:after { + content: "\2796"; + } + } + + #panel { + padding: 0 18px; + background-color: white; + max-height: 0; + overflow: hidden; + transition: max-height 0.2s ease-out; + } +} diff --git a/src/main/ts/src/app/core/components/accordion/accordion.component.ts b/src/main/ts/src/app/core/components/accordion/accordion.component.ts new file mode 100644 index 0000000..9b88743 --- /dev/null +++ b/src/main/ts/src/app/core/components/accordion/accordion.component.ts @@ -0,0 +1,27 @@ +import { Component, OnInit, Input, ViewChild, ElementRef } from '@angular/core'; + +@Component({ + selector: 'app-accordion', + templateUrl: './accordion.component.html', + styleUrls: ['./accordion.component.scss'] +}) +export class AccordionComponent implements OnInit { + @Input() title: string; + @Input() group: string; + @ViewChild('accordion', {static: true}) accordion: ElementRef; + @ViewChild('panel', {static: true}) panel: ElementRef; + + constructor() { } + + ngOnInit() { + this.accordion.nativeElement.addEventListener('click', () => { + this.accordion.nativeElement.classList.toggle('active'); + + const maxHeight = this.panel.nativeElement.style.maxHeight; + console.log(this.panel.nativeElement.style); + this.panel.nativeElement.style.maxHeight = !!maxHeight ? null : `${this.panel.nativeElement.scrollHeight}px`; + + + }); + } +} diff --git a/src/main/ts/src/app/core/entities.ts b/src/main/ts/src/app/core/entities.ts index 9712033..86ac985 100644 --- a/src/main/ts/src/app/core/entities.ts +++ b/src/main/ts/src/app/core/entities.ts @@ -25,11 +25,13 @@ export class SignUpDTO { export class ConfigurationFile { constructor( - public id: string + public id: string, + public content: string, + public htmlName: string ) {} public static new(): ConfigurationFile { - return new ConfigurationFile(''); + return new ConfigurationFile('', '', `${Math.random() * 100}`); } } @@ -37,7 +39,7 @@ export class Application { constructor( public id: string, public name: string, - public configurationFileList: Array + public configurationFileList: Array = [] ) {} public static new(): Application { diff --git a/src/main/ts/src/app/core/interceptors/abstract-interceptor.ts b/src/main/ts/src/app/core/interceptors/abstract-interceptor.ts new file mode 100644 index 0000000..2092be3 --- /dev/null +++ b/src/main/ts/src/app/core/interceptors/abstract-interceptor.ts @@ -0,0 +1,42 @@ +import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpErrorResponse } from '@angular/common/http'; +import { Router } from '@angular/router'; +import { Observable, throwError } from 'rxjs'; +import { catchError } from 'rxjs/operators'; +import { NotificationsComponent } from '../notifications/notifications.component'; + +/** + * Abstract interceptor that do redirection if an error code is intercepted. + */ +export abstract class AbstractInterceptor implements HttpInterceptor { + constructor( + protected router: Router + ) {} + + /** + * Returns the error code to intercept for redirection. + */ + protected abstract getHandledErrorCode(): number; + + /** + * Returns the error message to show in an error notification. + */ + protected abstract supplyErrorMessage(): string; + + /** + * Returns the angular route for redirection if the handled error code is intercepted. + */ + protected abstract supplyRedirectionRoute(): string; + + handleError = (error: HttpErrorResponse) => { + if (!!error && error.status === this.getHandledErrorCode()) { + NotificationsComponent.error(this.supplyErrorMessage()); + this.router.navigate([this.supplyRedirectionRoute()]); + } + + return throwError(error); + } + + intercept(req: HttpRequest, next: HttpHandler): Observable> { + return next.handle(req).pipe(catchError(this.handleError)); + } +} 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 index 4e23192..f7ff146 100644 --- a/src/main/ts/src/app/core/interceptors/service-unavailable.interceptor.ts +++ b/src/main/ts/src/app/core/interceptors/service-unavailable.interceptor.ts @@ -4,22 +4,23 @@ import { Observable, throwError } from 'rxjs'; import { Router } from '@angular/router'; import { catchError } from 'rxjs/operators'; import { NotificationsComponent } from '../notifications/notifications.component'; +import { AbstractInterceptor } from './abstract-interceptor'; @Injectable({providedIn: 'root'}) -export class ServiceUnavailableInterceptor implements HttpInterceptor { - +export class ServiceUnavailableInterceptor extends AbstractInterceptor { constructor( - private router: Router - ) {} + protected router: Router + ) { + super(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); - })); + protected getHandledErrorCode(): number { + return 504; + } + protected supplyErrorMessage(): string { + return 'Le serveur est actuellement indisponible. Veuillez contacter l\'administrateur.'; + } + protected supplyRedirectionRoute(): string { + return '/serviceUnavailable'; } } diff --git a/src/main/ts/src/app/core/interceptors/unauthorized.interceptor.ts b/src/main/ts/src/app/core/interceptors/unauthorized.interceptor.ts new file mode 100644 index 0000000..463ce48 --- /dev/null +++ b/src/main/ts/src/app/core/interceptors/unauthorized.interceptor.ts @@ -0,0 +1,22 @@ +import { Injectable } from '@angular/core'; +import { Router } from '@angular/router'; +import { AbstractInterceptor } from './abstract-interceptor'; + +@Injectable({providedIn: 'root'}) +export class UnauthorizedInterceptor extends AbstractInterceptor { + constructor( + protected router: Router + ) { + super(router); + } + + protected getHandledErrorCode(): number { + return 401; + } + protected supplyErrorMessage(): string { + return 'Impossible d\'accéder à cette ressource car vous n\'êtes pas connecté.'; + } + protected supplyRedirectionRoute(): string { + return '/login'; + } +} diff --git a/src/main/ts/src/app/core/notifications/notification-element/notification-element.component.ts b/src/main/ts/src/app/core/notifications/notification-element/notification-element.component.ts index 2ab19c7..d69da37 100644 --- a/src/main/ts/src/app/core/notifications/notification-element/notification-element.component.ts +++ b/src/main/ts/src/app/core/notifications/notification-element/notification-element.component.ts @@ -5,74 +5,74 @@ import { Component, Input, OnInit, ViewChild, ElementRef } from '@angular/core'; * Class which represents a notification in the notifications list. */ @Component({ - selector: 'app-notification-element', - templateUrl: 'notification-element.component.html', - styles: [` - #notification { - transition: all 0.7s ease-out; - position: relative; - } - .close { - position: absolute; - right: 7px; - top: 12px; - font-size: 19px; - opacity: 0; - } - #notification:hover .close { - opacity: 0.5; - } - `] + selector: 'app-notification-element', + templateUrl: 'notification-element.component.html', + styles: [` + #notification { + transition: all 0.7s ease-out; + position: relative; + } + .close { + position: absolute; + right: 7px; + top: 12px; + font-size: 19px; + opacity: 0; + } + #notification:hover .close { + opacity: 0.5; + } + `] }) export class NotificationElement implements OnInit { - /** - * The notification model. - */ - @Input() model: NotificationModel; - /** - * The notification DOM element. - */ - @ViewChild('notification', {static: true}) notification: ElementRef; + /** + * The notification model. + */ + @Input() model: NotificationModel; + /** + * The notification DOM element. + */ + @ViewChild('notification', {static: true}) notification: ElementRef; - /** - * Sets the DOM element in the model object and plays with opacity. - */ - ngOnInit(): void { - this.model.notification = this.notification; + /** + * Sets the DOM element in the model object and plays with opacity. + */ + ngOnInit(): void { + this.model.notification = this.notification; - this.notification.nativeElement.style.opacity = 0; - setTimeout(() => { - this.notification.nativeElement.style.opacity = 1; - }, 100); - } + this.notification.nativeElement.style.opacity = 0; + setTimeout(() => { + this.notification.nativeElement.style.opacity = 1; + }, 100); + } } /** * Class which represents the notification model. */ export class NotificationModel { - /** - * Element which represents the DOM element of the notification element. - */ - notification: ElementRef; + /** + * Element which represents the DOM element of the notification element. + */ + notification: ElementRef; - /** - * Default constructor. - * @param {string} content The message of the notification. - * @param {NotificationClass} notificationClass The category of the notification (info, error...). - */ - constructor( - public content: string, - public notificationClass: NotificationClass - ) {} + /** + * Default constructor. + * @param {string} content The message of the notification. + * @param {NotificationClass} notificationClass The category of the notification (info, error...). + */ + constructor( + public content: string, + public notificationClass: NotificationClass + ) {} - /** - * Hides the notification DOM element. - */ - public hide(): void { - this.notification.nativeElement.style.opacity = 0; - setTimeout(() => { - this.notification.nativeElement.style.display = 'none'; - }, 800); - } + /** + * Hides the notification DOM element. + */ + public hide(): void { + this.notification.nativeElement.style.opacity = 0; + setTimeout(() => { + this.notification.nativeElement.style.display = 'none'; + }, 800); + } } diff --git a/src/main/ts/src/app/core/notifications/notifications.component.ts b/src/main/ts/src/app/core/notifications/notifications.component.ts index 64e2f10..af8ff98 100644 --- a/src/main/ts/src/app/core/notifications/notifications.component.ts +++ b/src/main/ts/src/app/core/notifications/notifications.component.ts @@ -6,103 +6,103 @@ import { Component, OnInit } from '@angular/core'; * Class which offers the notifications service. */ @Component({ - selector: 'app-notifications', - templateUrl: 'notifications.component.html', - styles: [` - #notification-container { - position: fixed; - top: 50px; - right: 20px; - width: 300px; - z-index: 1100; - } - `] + selector: 'app-notifications', + templateUrl: 'notifications.component.html', + styles: [` + #notification-container { + position: fixed; + top: 50px; + right: 20px; + width: 300px; + z-index: 1100; + } + `] }) export class NotificationsComponent implements OnInit { - /** - * Singleton of the notification service. - */ - private static component: NotificationsComponent; + /** + * Singleton of the notification service. + */ + private static component: NotificationsComponent; - /** - * List of notifications model. - */ - notificationList: Array = []; + /** + * List of notifications model. + */ + notificationList: Array = []; - /** - * Creates an error notification. - * @param {string} message The content of the notification. - */ - public static error(message: string): void { - NotificationsComponent.notif(message, NotificationClasses.Error); - } + /** + * Creates an error notification. + * @param {string} message The content of the notification. + */ + public static error(message: string): void { + NotificationsComponent.notif(message, NotificationClasses.Error); + } - /** - * Creates a warning notification. - * @param {string} message The content of the notification. - */ - public static warn(message: string): void { - NotificationsComponent.notif(message, NotificationClasses.Warn); - } + /** + * Creates a warning notification. + * @param {string} message The content of the notification. + */ + public static warn(message: string): void { + NotificationsComponent.notif(message, NotificationClasses.Warn); + } - /** - * Creates an info notification. - * @param {string} message The content of the notification. - */ - public static info(message: string): void { - NotificationsComponent.notif(message, NotificationClasses.Info); - } + /** + * Creates an info notification. + * @param {string} message The content of the notification. + */ + public static info(message: string): void { + NotificationsComponent.notif(message, NotificationClasses.Info); + } - /** - * Creates a success notification. - * @param {string} message The content of the notification. - */ - public static success(message: string): void { - NotificationsComponent.notif(message, NotificationClasses.Success); - } + /** + * Creates a success notification. + * @param {string} message The content of the notification. + */ + public static success(message: string): void { + NotificationsComponent.notif(message, NotificationClasses.Success); + } - /** - * Create a notification. The {@code notifClass} param defines the category of - * the notification (info, error...). - * @param {string} message The content of the notification. - * @param {NotificationClass} notifClass The category of the notification. - */ - private static notif(message: string, notifClass: NotificationClass): void { - const elem = new NotificationModel(message, notifClass); + /** + * Create a notification. The {@code notifClass} param defines the category of + * the notification (info, error...). + * @param {string} message The content of the notification. + * @param {NotificationClass} notifClass The category of the notification. + */ + private static notif(message: string, notifClass: NotificationClass): void { + const elem = new NotificationModel(message, notifClass); - NotificationsComponent.component.notificationList.push(elem); + NotificationsComponent.component.notificationList.push(elem); - setTimeout(() => { - elem.hide(); + setTimeout(() => { + elem.hide(); - setTimeout(() => { - NotificationsComponent.clearNotificationList(); - }, 900); - }, 4500); - } + setTimeout(() => { + NotificationsComponent.clearNotificationList(); + }, 900); + }, 4500); + } - /** - * Clears the consumed notifications in the list. - * When a notification is created, a cooldown is set to hide it after a certain time period. - * In this cooldown, the notification have only its display as {@code none}, but the - * notification isn't remove from the list. This method removes it. - */ - private static clearNotificationList(): void { - NotificationsComponent.component.notificationList.forEach(elem => { - if (elem.notification.nativeElement.style.display === 'none') { - const index = NotificationsComponent.component.notificationList.indexOf(elem); - if (index > -1) { - NotificationsComponent.component.notificationList.splice(index, 1); - } - } - }); - } + /** + * Clears the consumed notifications in the list. + * When a notification is created, a cooldown is set to hide it after a certain time period. + * In this cooldown, the notification have only its display as {@code none}, but the + * notification isn't remove from the list. This method removes it. + */ + private static clearNotificationList(): void { + NotificationsComponent.component.notificationList.forEach(elem => { + if (elem.notification.nativeElement.style.display === 'none') { + const index = NotificationsComponent.component.notificationList.indexOf(elem); + if (index > -1) { + NotificationsComponent.component.notificationList.splice(index, 1); + } + } + }); + } - /** - * Set the reference of the singleton here because this component - * is created at the application startup. - */ - ngOnInit(): void { - NotificationsComponent.component = this; - } + /** + * Set the reference of the singleton here because this component + * is created at the application startup. + */ + ngOnInit(): void { + NotificationsComponent.component = this; + } } diff --git a/src/main/ts/src/app/core/services/application.service.ts b/src/main/ts/src/app/core/services/application.service.ts new file mode 100644 index 0000000..dff5dbb --- /dev/null +++ b/src/main/ts/src/app/core/services/application.service.ts @@ -0,0 +1,21 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs'; +import { Application } from '../entities'; + +@Injectable({ + providedIn: 'root' +}) +export class ApplicationService { + constructor( + private httpClient: HttpClient + ) {} + + findAll(): Observable> { + return this.httpClient.get>(`/api/applications`); + } + + findById(appId: string): Observable { + return this.httpClient.get(`/api/applications/${appId}`); + } +} 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 deleted file mode 100644 index 79eb548..0000000 --- a/src/main/ts/src/app/core/services/user.service.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* 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/header/header.component.html b/src/main/ts/src/app/header/header.component.html index 123504d..2c8a999 100644 --- a/src/main/ts/src/app/header/header.component.html +++ b/src/main/ts/src/app/header/header.component.html @@ -1,6 +1,6 @@
- @@ -22,3 +22,4 @@
+ diff --git a/src/main/ts/src/app/header/header.component.ts b/src/main/ts/src/app/header/header.component.ts index 515a021..266cf12 100644 --- a/src/main/ts/src/app/header/header.component.ts +++ b/src/main/ts/src/app/header/header.component.ts @@ -1,3 +1,4 @@ +import { SideNavComponent } from './side-nav/side-nav.component'; import { AuthService } from './../core/services/auth.service'; import { NotificationsComponent } from './../core/notifications/notifications.component'; import { Component, OnInit, ViewChild } from '@angular/core'; @@ -9,8 +10,6 @@ import { LoginComponent } from '../login/login.component'; styleUrls: ['./header.component.scss'] }) export class HeaderComponent { - @ViewChild('loginForm', {static: true}) loginForm: LoginComponent; - constructor( private authService: AuthService ) {} diff --git a/src/main/ts/src/app/header/side-nav/side-nav-element/side-nav-element.component.html b/src/main/ts/src/app/header/side-nav/side-nav-element/side-nav-element.component.html new file mode 100644 index 0000000..0a51de3 --- /dev/null +++ b/src/main/ts/src/app/header/side-nav/side-nav-element/side-nav-element.component.html @@ -0,0 +1,6 @@ + +
+ + {{text}} +
+
diff --git a/src/main/ts/src/app/header/side-nav/side-nav-element/side-nav-element.component.scss b/src/main/ts/src/app/header/side-nav/side-nav-element/side-nav-element.component.scss new file mode 100644 index 0000000..f65de47 --- /dev/null +++ b/src/main/ts/src/app/header/side-nav/side-nav-element/side-nav-element.component.scss @@ -0,0 +1,21 @@ +a, a:hover, a:visited, a:focus { + color: inherit; +} + +#element { + padding: 15px 25px; + padding-left: 60px; + position: relative; + + &:hover { + background-color: #402c25; + } + + + .element-icon { + left: 25px; + margin-right: 15px; + position: absolute; + top: 19px; + } +} diff --git a/src/main/ts/src/app/header/side-nav/side-nav-element/side-nav-element.component.ts b/src/main/ts/src/app/header/side-nav/side-nav-element/side-nav-element.component.ts new file mode 100644 index 0000000..60b3b4c --- /dev/null +++ b/src/main/ts/src/app/header/side-nav/side-nav-element/side-nav-element.component.ts @@ -0,0 +1,20 @@ +import { SIDE_NAV_WIDTH } from './../side-nav.component'; +import { Component, OnInit, Input, ViewChild, ElementRef } from '@angular/core'; + +@Component({ + selector: 'app-side-nav-element', + templateUrl: './side-nav-element.component.html', + styleUrls: ['./side-nav-element.component.scss'] +}) +export class SideNavElementComponent implements OnInit { + @ViewChild('element', {static: true}) element: ElementRef; + @Input() text: string; + @Input() icon: string; + @Input() routerLink: string; + + constructor() {} + + ngOnInit(): void { + this.element.nativeElement.style.width = SIDE_NAV_WIDTH; + } +} diff --git a/src/main/ts/src/app/header/side-nav/side-nav.component.html b/src/main/ts/src/app/header/side-nav/side-nav.component.html new file mode 100644 index 0000000..524ccfa --- /dev/null +++ b/src/main/ts/src/app/header/side-nav/side-nav.component.html @@ -0,0 +1,10 @@ +
+
+ + +
+ + + +
+
diff --git a/src/main/ts/src/app/header/side-nav/side-nav.component.scss b/src/main/ts/src/app/header/side-nav/side-nav.component.scss new file mode 100644 index 0000000..12456e3 --- /dev/null +++ b/src/main/ts/src/app/header/side-nav/side-nav.component.scss @@ -0,0 +1,53 @@ +#sideNav { + background-color: #5d4037; + color: white; + height: 100%; + left: 0; + overflow-x: hidden; + padding-top: 78px; + position: fixed; + top: 0; + transition: 0.3s; + width: 0; + z-index: 1050; + + #sideNav-close { + background-color: transparent; + border: 0; + position: absolute; + right: 30px; + top: 31px; + z-index: 1; + + &:hover { + cursor: pointer; + color: #ddd; + } + } + + #header { + font-size: 2rem; + left: 0; + padding: 15px; + position: absolute; + text-align: center; + top: 0; + } + + #content { + + } +} + +#overlay { + background-color: #000; + left: 0; + height: 100%; + opacity: 0; + top: 0; + position: fixed; + transition: 0.5s; + visibility: hidden; + width: 100%; + z-index: 1049; +} diff --git a/src/main/ts/src/app/header/side-nav/side-nav.component.ts b/src/main/ts/src/app/header/side-nav/side-nav.component.ts new file mode 100644 index 0000000..f2469ca --- /dev/null +++ b/src/main/ts/src/app/header/side-nav/side-nav.component.ts @@ -0,0 +1,42 @@ +import { Component, OnInit, ViewChild, ElementRef } from '@angular/core'; + +export const SIDE_NAV_WIDTH = '350px'; + +@Component({ + selector: 'app-side-nav', + templateUrl: './side-nav.component.html', + styleUrls: ['./side-nav.component.scss'] +}) +export class SideNavComponent implements OnInit { + appSideNavElementList = [{ + text: 'Gestion des utilisateurs', + icon: 'users-cog', + link: 'management/users' + }, { + text: 'Gestion des applications', + icon: 'window-restore', + link: 'management/applications' + }]; + @ViewChild('sideNav', {static: true}) sideNavDiv: ElementRef; + @ViewChild('overlay', {static: true}) overlay: ElementRef; + @ViewChild('header', {static: true}) header: ElementRef; + + + constructor() {} + + ngOnInit() { + this.header.nativeElement.style.width = SIDE_NAV_WIDTH; + } + + open(): void { + this.sideNavDiv.nativeElement.style.width = SIDE_NAV_WIDTH; + this.overlay.nativeElement.style.visibility = 'visible'; + this.overlay.nativeElement.style.opacity = 0.5; + } + + close(): void { + this.sideNavDiv.nativeElement.style.width = '0px'; + this.overlay.nativeElement.style.visibility = 'hidden'; + this.overlay.nativeElement.style.opacity = 0; + } +} diff --git a/src/main/ts/src/app/management/app-management/app-edition/app-edition.component.html b/src/main/ts/src/app/management/app-management/app-edition/app-edition.component.html new file mode 100644 index 0000000..22b2cc9 --- /dev/null +++ b/src/main/ts/src/app/management/app-management/app-edition/app-edition.component.html @@ -0,0 +1,107 @@ + + + diff --git a/src/main/ts/src/app/management/app-management/app-edition/app-edition.component.scss b/src/main/ts/src/app/management/app-management/app-edition/app-edition.component.scss new file mode 100644 index 0000000..60a24d9 --- /dev/null +++ b/src/main/ts/src/app/management/app-management/app-edition/app-edition.component.scss @@ -0,0 +1,3 @@ +app-accordion { + width: 100%; +} diff --git a/src/main/ts/src/app/management/app-management/app-edition/app-edition.component.ts b/src/main/ts/src/app/management/app-management/app-edition/app-edition.component.ts new file mode 100644 index 0000000..31e8720 --- /dev/null +++ b/src/main/ts/src/app/management/app-management/app-edition/app-edition.component.ts @@ -0,0 +1,49 @@ +import { NotificationsComponent } from 'src/app/core/notifications/notifications.component'; +import { Application, ConfigurationFile } from './../../../core/entities'; +import { ApplicationService } from './../../../core/services/application.service'; +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute, Router } from '@angular/router'; + +@Component({ + selector: 'app-app-edition', + templateUrl: './app-edition.component.html', + styleUrls: ['./app-edition.component.scss'] +}) +export class AppEditionComponent implements OnInit { + model: Application = Application.new(); + + constructor( + private activatedRoute: ActivatedRoute, + private applicationService: ApplicationService, + private router: Router + ) {} + + ngOnInit() { + const appId = this.activatedRoute.snapshot.paramMap.get('appId'); + if (!!appId) { + this.applicationService.findById(appId).subscribe(application => { + this.model = application; + }, error => { + console.error(error); + NotificationsComponent.error('Une erreur est survenue lors de la récupération des données de l\'application.'); + this.router.navigate(['/management/applications']); + }); + } + } + + onSubmit() { + + } + + addConfigurationFile(): void { + if (!!this.model.configurationFileList) { + this.model.configurationFileList.push(ConfigurationFile.new()); + } else { + this.model.configurationFileList = [ConfigurationFile.new()]; + } + } + + removeConfigurationFile(confFile: ConfigurationFile): void { + this.model.configurationFileList = this.model.configurationFileList.filter(c => c !== confFile); + } +} diff --git a/src/main/ts/src/app/management/app-management/app-management.component.html b/src/main/ts/src/app/management/app-management/app-management.component.html new file mode 100644 index 0000000..35dfb56 --- /dev/null +++ b/src/main/ts/src/app/management/app-management/app-management.component.html @@ -0,0 +1,27 @@ + + Chargement des applications... + + + + + + diff --git a/src/main/ts/src/app/management/app-management/app-management.component.scss b/src/main/ts/src/app/management/app-management/app-management.component.scss new file mode 100644 index 0000000..7bfe029 --- /dev/null +++ b/src/main/ts/src/app/management/app-management/app-management.component.scss @@ -0,0 +1,12 @@ +.row { + .app-card { + // width: 300px; + // margin: 25px auto; + margin-top: 25px; + margin-bottom: 25px; + + &.col-12 { + max-width: 90%; + } + } +} diff --git a/src/main/ts/src/app/management/app-management/app-management.component.ts b/src/main/ts/src/app/management/app-management/app-management.component.ts new file mode 100644 index 0000000..c301436 --- /dev/null +++ b/src/main/ts/src/app/management/app-management/app-management.component.ts @@ -0,0 +1,32 @@ +import { ApplicationService } from './../../core/services/application.service'; +import { Component, OnInit } from '@angular/core'; +import { Application } from 'src/app/core/entities'; +import { NotificationsComponent } from 'src/app/core/notifications/notifications.component'; + +@Component({ + selector: 'app-app-management', + templateUrl: './app-management.component.html', + styleUrls: ['./app-management.component.scss'] +}) +export class AppManagementComponent implements OnInit { + appList: Array = []; + loading: boolean; + + constructor( + private applicationService: ApplicationService + ) {} + + ngOnInit(): void { + this.loading = true; + this.applicationService.findAll().subscribe(appList => { + this.appList = appList; + appList.forEach(a => this.appList.push(a)); + appList.forEach(a => this.appList.push(a)); + appList.forEach(a => this.appList.push(a)); + appList.forEach(a => this.appList.push(a)); + }, error => { + console.error(error); + NotificationsComponent.error('Une erreur est survenue lors du chargement des applications.'); + }).add(() => this.loading = false); + } +} diff --git a/src/main/ts/src/app/management/user-management/user-management.component.html b/src/main/ts/src/app/management/user-management/user-management.component.html new file mode 100644 index 0000000..e453d52 --- /dev/null +++ b/src/main/ts/src/app/management/user-management/user-management.component.html @@ -0,0 +1,3 @@ +

+ user-management works! +

diff --git a/src/main/ts/src/app/management/user-management/user-management.component.scss b/src/main/ts/src/app/management/user-management/user-management.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/main/ts/src/app/management/user-management/user-management.component.ts b/src/main/ts/src/app/management/user-management/user-management.component.ts new file mode 100644 index 0000000..a374bb2 --- /dev/null +++ b/src/main/ts/src/app/management/user-management/user-management.component.ts @@ -0,0 +1,15 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-user-management', + templateUrl: './user-management.component.html', + styleUrls: ['./user-management.component.scss'] +}) +export class UserManagementComponent implements OnInit { + + constructor() { } + + ngOnInit() { + } + +} diff --git a/src/main/ts/src/styles.scss b/src/main/ts/src/styles.scss index f26f6f4..9faee64 100644 --- a/src/main/ts/src/styles.scss +++ b/src/main/ts/src/styles.scss @@ -18,3 +18,20 @@ a, button { html { height: 100%; } + +.btn-floating { + box-shadow: 0 5px 11px 0 rgba(0,0,0,.18),0 4px 15px 0 rgba(0,0,0,.15); + position: relative; + z-index: 1; + vertical-align: middle; + display: inline-block; + overflow: hidden; + transition: all .2s ease-in-out; + margin: 10px; + border-radius: 50%; + padding: 0; + cursor: pointer; + width: 47px; + height: 47px; + border: 0px; +}