Update to Angular v19 and fixing (#3)

Reviewed-on: #3
Co-authored-by: sandre <perso@sebastienandre.com>
Co-committed-by: sandre <perso@sebastienandre.com>
This commit was merged in pull request #3.
This commit is contained in:
2026-01-20 10:56:31 +00:00
committed by sandre
parent af44e50f54
commit 137b2ab1fc
117 changed files with 3496 additions and 2471 deletions

View File

@@ -0,0 +1,33 @@
import { inject, OnInit } from "@angular/core";
import { IconResolver, MatIconRegistry } from "@angular/material/icon";
import { DomSanitizer } from "@angular/platform-browser";
import { TranslateService } from "@ngx-translate/core";
import { ServiceComm } from "../services/service-comm.service";
@decorator
export class BaseComponent implements OnInit {
private titleId: string = "???";
protected serviceComm: ServiceComm;
protected translateService: TranslateService;
constructor(titleId: string) {
let iconRegistry = inject(MatIconRegistry);
let sanitizer = inject(DomSanitizer);
const resolver: IconResolver = (name) =>
sanitizer.bypassSecurityTrustResourceUrl(`/assets/icon/${name}.svg`);
iconRegistry.addSvgIconResolver(resolver);
this.serviceComm = inject(ServiceComm);
this.translateService = inject(TranslateService);
this.titleId = titleId;
}
ngOnInit() {
this.translateService.get(this.titleId).subscribe((data) => {
this.serviceComm.updatedComponentTitle(data);
// this.serviceComm.componentTitle.subscribe((title) => (this.title = data));
});
}
}
function decorator(target: typeof BaseComponent): void | typeof BaseComponent {}

View File

@@ -1,4 +1,4 @@
.hamburger__icon {
/* .hamburger__icon {
position: relative;
height: 1rem;
margin-right: 1rem;
@@ -8,7 +8,7 @@
.hamburger__icon__fill {
fill: #424242
}
} */
.navigation.side-menu-active {
left: 0px;
@@ -53,4 +53,4 @@
.mat-icon {
margin-right: 15px;
}
}

View File

@@ -1,76 +1,190 @@
<mat-toolbar *ngIf="this.show()">
<button mat-icon-button (click)="snav.toggle()"><mat-icon>menu</mat-icon></button>
<h2>{{ title }}</h2>
<mat-select (selectionChange)="switchLang($event)" [(value)]="selectedLanguageFlag"
style="margin-left:50px; width: 60px;" >
<mat-icon svgIcon="menu" (click)="snav.toggle()"></mat-icon>
<h2>{{ translatedTitle }}</h2>
<mat-select
[(value)]="selectedLanguageFlag"
(selectionChange)="switchLang($event)"
style="margin-left: 50px; width: 100px"
>
<mat-select-trigger>
<img src="{{ 'assets/img/' + selectedLanguageFlag + '.svg' }}" style="width: 30px;">
<img
src="{{ 'assets/img/' + selectedLanguageFlag + '.svg' }}"
style="width: 30px"
/>
</mat-select-trigger>
<mat-option value="fr">
<img src="assets/img/fr.svg" style="width: 30px;">
<img src="assets/img/fr.svg" style="width: 30px" />
</mat-option>
<mat-option value="en">
<img src="assets/img/en.svg" style="width: 30px;">
<img src="assets/img/en.svg" style="width: 30px" />
</mat-option>
</mat-select>
</mat-toolbar>
<mat-sidenav-container>
<mat-sidenav #snav mode="over" style="padding: 0 20px 0 10px;">
<mat-sidenav #snav mode="over" style="padding: 0 20px 0 10px">
<mat-nav-list>
<mat-icon aria-hidden="false" aria-label="Summary">timeline</mat-icon>
<a routerLink="/summary" routerLinkActive="active" (click)="snav.toggle()" skipLocationChange>{{ 'App_Nav_Summary' | translate }}</a>
<hr class="splitter">
<mat-icon
aria-hidden="false"
aria-label="Summary"
svgIcon="summary"
></mat-icon>
<a
routerLink="/summary"
routerLinkActive="active"
(click)="snav.toggle()"
skipLocationChange
>{{ "App_Nav_Summary" | translate }}</a
>
<hr class="splitter" />
</mat-nav-list>
<mat-nav-list>
<mat-icon aria-hidden="false" aria-label="List of jumps">list_alt</mat-icon>
<a routerLink="/jumps" routerLinkActive="active" (click)="snav.toggle()" skipLocationChange>{{ 'App_Nav_Jumps' | translate }}</a>
<mat-icon
aria-hidden="false"
aria-label="List of jumps"
svgIcon="list"
></mat-icon>
<a
routerLink="/jumps"
routerLinkActive="active"
(click)="snav.toggle()"
skipLocationChange
>{{ "App_Nav_Jumps" | translate }}</a
>
</mat-nav-list>
<mat-nav-list>
<mat-icon aria-hidden="false" aria-label="Add jumps">add_circle</mat-icon>
<a routerLink="/newjump" routerLinkActive="active" (click)="snav.toggle()" skipLocationChange>{{ 'App_Nav_NewJump' | translate }}</a>
<hr class="splitter">
<mat-icon
aria-hidden="false"
aria-label="Add jumps"
svgIcon="add"
></mat-icon>
<a
routerLink="/newjump"
routerLinkActive="active"
(click)="snav.toggle()"
skipLocationChange
>{{ "App_Nav_NewJump" | translate }}</a
>
<hr class="splitter" />
</mat-nav-list>
<mat-nav-list>
<mat-icon aria-hidden="false" aria-label="List of tunnel flights">list_alt</mat-icon>
<a routerLink="/tunnelFlights" routerLinkActive="active" (click)="snav.toggle()" skipLocationChange>{{ 'App_Nav_TunnelFlights' | translate }}</a>
<mat-icon
aria-hidden="false"
aria-label="List of tunnel flights"
svgIcon="list"
></mat-icon>
<a
routerLink="/tunnelFlights"
routerLinkActive="active"
(click)="snav.toggle()"
skipLocationChange
>{{ "App_Nav_TunnelFlights" | translate }}</a
>
</mat-nav-list>
<mat-nav-list>
<mat-icon aria-hidden="false" aria-label="Add flights in tunnel">add_circle</mat-icon>
<a routerLink="/newTunnelFlight" routerLinkActive="active" (click)="snav.toggle()" skipLocationChange>{{ 'App_Nav_NewTunnelFlight' | translate }}</a>
<hr class="splitter">
<mat-icon
aria-hidden="false"
aria-label="Add flights in tunnel"
svgIcon="add"
></mat-icon>
<a
routerLink="/newTunnelFlight"
routerLinkActive="active"
(click)="snav.toggle()"
skipLocationChange
>{{ "App_Nav_NewTunnelFlight" | translate }}</a
>
<hr class="splitter" />
</mat-nav-list>
<mat-nav-list>
<mat-icon aria-hidden="false" aria-label="Dropzones">store</mat-icon>
<a routerLink="/dzs" routerLinkActive="active" (click)="snav.toggle()" skipLocationChange>{{ 'App_Nav_Dzs' | translate }}</a>
<mat-icon
aria-hidden="false"
aria-label="Dropzones"
svgIcon="dz"
></mat-icon>
<a
routerLink="/dzs"
routerLinkActive="active"
(click)="snav.toggle()"
skipLocationChange
>{{ "App_Nav_Dzs" | translate }}</a
>
</mat-nav-list>
<mat-nav-list>
<mat-icon aria-hidden="false" aria-label="Aircrafts">airplanemode_active</mat-icon>
<a routerLink="/aircrafts" routerLinkActive="active" (click)="snav.toggle()" skipLocationChange>{{ 'App_Nav_Aircrafts' | translate }}</a>
<mat-icon
aria-hidden="false"
aria-label="Aircrafts"
svgIcon="aircraft"
></mat-icon>
<a
routerLink="/aircrafts"
routerLinkActive="active"
(click)="snav.toggle()"
skipLocationChange
>{{ "App_Nav_Aircrafts" | translate }}</a
>
</mat-nav-list>
<mat-nav-list>
<mat-icon aria-hidden="false" aria-label="List of jump types">flight_land</mat-icon>
<a routerLink="/jumpTypes" routerLinkActive="active" (click)="snav.toggle()" skipLocationChange>{{ 'App_Nav_JumpTypes' | translate }}</a>
<mat-icon
aria-hidden="false"
aria-label="List of jump types"
svgIcon="flight_land"
></mat-icon>
<a
routerLink="/jumpTypes"
routerLinkActive="active"
(click)="snav.toggle()"
skipLocationChange
>{{ "App_Nav_JumpTypes" | translate }}</a
>
</mat-nav-list>
<mat-nav-list>
<mat-icon aria-hidden="false" aria-label="List of gears">settings_input_antenna</mat-icon>
<a routerLink="/gears" routerLinkActive="active" (click)="snav.toggle()" skipLocationChange>{{ 'App_Nav_Gears' | translate }}</a>
<mat-icon
aria-hidden="false"
aria-label="List of gears"
svgIcon="gear"
></mat-icon>
<a
routerLink="/gears"
routerLinkActive="active"
(click)="snav.toggle()"
skipLocationChange
>{{ "App_Nav_Gears" | translate }}</a
>
</mat-nav-list>
<mat-nav-list *ngIf="currentUser">
<hr class="splitter">
<mat-icon aria-hidden="false" aria-label="User account">account_box</mat-icon>
<a routerLink="/user" routerLinkActive="active" (click)="snav.toggle()" skipLocationChange>
<hr class="splitter" />
<mat-icon
aria-hidden="false"
aria-label="User account"
svgIcon="account"
></mat-icon>
<a
routerLink="/user"
routerLinkActive="active"
(click)="snav.toggle()"
skipLocationChange
>
{{ this.currentUser.firstName }} {{ this.currentUser.lastName }}
</a>
</mat-nav-list>
<mat-nav-list *ngIf="currentUser">
<mat-icon aria-hidden="false" aria-label="To logout">logout</mat-icon>
<span (click)="snav.toggle(); logout()" style="cursor: pointer;">{{ 'App_Nav_Logout' | translate }}</span>
<mat-icon
aria-hidden="false"
aria-label="To logout"
svgIcon="logout"
></mat-icon>
<span (click)="snav.toggle(); logout()" style="cursor: pointer">{{
"App_Nav_Logout" | translate
}}</span>
</mat-nav-list>
</mat-sidenav>
<mat-sidenav-content>
<router-outlet></router-outlet>
<footer style="text-align: right;">{{ 'App_Footer' | translate }}{{ version }} - &#64;Séb</footer>
<footer style="text-align: right">
{{ "App_Footer" | translate }}{{ version }} - &#64;Séb
</footer>
</mat-sidenav-content>
</mat-sidenav-container>
</mat-sidenav-container>

View File

@@ -1,6 +1,22 @@
import { Component, OnInit } from "@angular/core";
import { Router } from "@angular/router";
import { TranslateService } from "@ngx-translate/core";
import { Component, inject, OnInit } from "@angular/core";
import { Router, RouterLink, RouterOutlet } from "@angular/router";
import { CommonModule } from "@angular/common";
import { DomSanitizer } from "@angular/platform-browser";
import { MatToolbarModule } from "@angular/material/toolbar";
import {
IconResolver,
MatIconModule,
MatIconRegistry,
} from "@angular/material/icon";
import { MatSelectModule } from "@angular/material/select";
import { MatOptionModule } from "@angular/material/core";
import { MatSidenavModule } from "@angular/material/sidenav";
import { MatListModule } from "@angular/material/list";
import {
TranslateService,
TranslateModule,
TranslatePipe,
} from "@ngx-translate/core";
import { User } from "../models/user";
import { CacheApiKey } from "../models/cache-api-key.enum";
@@ -11,42 +27,62 @@ import { ConfigurationHelper } from "../services/configuration-helper";
import { ServiceCacheApi } from "../services/service-cache-api.service";
@Component({
selector: "app-root",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"],
standalone: false
selector: "app-root",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"],
imports: [
CommonModule,
MatToolbarModule,
MatIconModule,
MatSelectModule,
MatOptionModule,
MatSidenavModule,
MatListModule,
RouterOutlet,
RouterLink,
TranslateModule,
TranslatePipe,
],
})
export class AppComponent implements OnInit {
public title = "app";
public translatedTitle = "???";
public currentUser: User;
public version: string;
public selectedLanguageFlag: string;
constructor(private router: Router,
private authenticationService: AuthenticationService,
private serviceComm: ServiceComm,
private serviceCacheApi : ServiceCacheApi,
private translateService: TranslateService)
{
this.authenticationService.currentUser.subscribe(user => {
constructor(
private router: Router,
private authenticationService: AuthenticationService,
private serviceComm: ServiceComm,
private serviceCacheApi: ServiceCacheApi,
private translateService: TranslateService,
) {
const sanitizer = inject(DomSanitizer);
const resolver: IconResolver = (name) =>
sanitizer.bypassSecurityTrustResourceUrl(`/assets/icon/${name}.svg`);
const iconRegistry = inject(MatIconRegistry);
iconRegistry.addSvgIconResolver(resolver);
this.authenticationService.currentUser.subscribe((user) => {
if (user) {
this.currentUser = user;
this.translateService.addLangs(['en', 'fr']);
this.translateService.addLangs(["en", "fr"]);
this.translateService.use(user.language);
this.selectedLanguageFlag = user.language;
}
});
ConfigurationHelper.settings.subscribe(settings =>
{
if (settings != null) {
this.version = settings.version;
}
ConfigurationHelper.settings.subscribe((settings) => {
if (settings != null) {
this.version = settings.version;
}
});
}
ngOnInit() {
this.serviceComm.componentTitle.subscribe(title => (this.title = title));
this.serviceComm.componentTitle.subscribe(
(title) => (this.translatedTitle = title),
);
}
public show() {

View File

@@ -0,0 +1,73 @@
import { inject, provideAppInitializer } from "@angular/core";
import { ApplicationConfig, provideZoneChangeDetection } from "@angular/core";
import { provideRouter } from "@angular/router";
import { DatePipe } from "@angular/common";
import { provideHttpClient, withInterceptors } from "@angular/common/http";
import { DateService } from "../services/date.service";
import { AircraftService } from "../services/aircraft.service";
import { DropzoneService } from "../services/dropzone.service";
import { GearService } from "../services/gear.service";
import { JumpService } from "../services/jump.service";
import { JumpTypeService } from "../services/jump-type.service";
import { StatsService } from "../services/stats.service";
import { ServiceComm } from "../services/service-comm.service";
import { RequestCache } from "../services/request-cache.service";
import { ImageService } from "../services/image.service";
import { ConfigurationHelper } from "../services/configuration-helper";
import { ServiceCacheApi } from "../services/service-cache-api.service";
import { TunnelService } from "../services/tunnel.service";
import { TunnelFlightService } from "../services/tunnel-flight.service";
import { provideCharts, withDefaultRegisterables } from "ng2-charts";
import { JwtAuthInterceptor } from "../interceptor/jwt-auth.interceptor";
import { ErrorInterceptor } from "../interceptor/error.interceptor";
import { environment } from "../environments/environment";
import { routes } from "./app.routes";
import { provideTranslateService } from "@ngx-translate/core";
import { provideTranslateHttpLoader } from "@ngx-translate/http-loader";
// Déclaration de la fonction d'initialisation de la configuration
export function initConfig(configService: ConfigurationHelper) {
return () => configService.load(environment.env);
}
export const appConfig: ApplicationConfig = {
providers: [
TunnelService,
TunnelFlightService,
ImageService,
AircraftService,
DropzoneService,
GearService,
JumpService,
JumpTypeService,
StatsService,
ServiceComm,
DateService,
RequestCache,
ConfigurationHelper,
DatePipe,
ServiceCacheApi,
provideAppInitializer(() => {
const initializerFn = initConfig(inject(ConfigurationHelper));
return initializerFn();
}),
provideHttpClient(withInterceptors([JwtAuthInterceptor, ErrorInterceptor])),
provideCharts(withDefaultRegisterables()),
provideZoneChangeDetection({ eventCoalescing: true }),
provideRouter(routes),
provideHttpClient(),
provideTranslateService({
loader: provideTranslateHttpLoader({
prefix: "/assets/i18n/",
suffix: ".json",
}),
fallbackLang: "en",
lang: "en",
}),
],
};

View File

@@ -1,240 +0,0 @@
import { BrowserModule } from "@angular/platform-browser";
import { NgModule, inject, provideAppInitializer } from "@angular/core";
import { RouterModule, Routes } from "@angular/router";
import {
HttpClient,
HttpClientModule,
HTTP_INTERCEPTORS,
} from "@angular/common/http";
import { DatePipe } from "@angular/common";
import { TranslateLoader, TranslateModule } from "@ngx-translate/core";
import { TranslateHttpLoader } from "@ngx-translate/http-loader";
import { AppComponent } from "./app.component";
import { environment } from "../environments/environment";
import { SummaryComponent } from "./summary/summary.component";
import { ListOfJumpsComponent } from "./list-of-jumps/list-of-jumps.component";
import { ListOfDzsComponent } from "./list-of-dzs/list-of-dzs.component";
import { NewJumpComponent } from "./new-jump/new-jump.component";
import { ListOfAircraftsComponent } from "./list-of-aircrafts/list-of-aircrafts.component";
import { ListOfJumpTypesComponent } from "./list-of-jump-types/list-of-jump-types.component";
import { ListOfGearsComponent } from "./list-of-gears/list-of-gears.component";
import { NewAircraftComponent } from "./new-aircraft/new-aircraft.component";
import { NewGearComponent } from "./new-gear/new-gear.component";
import { NewDropZoneComponent } from "./new-drop-zone/new-drop-zone.component";
import { NewJumpTypeComponent } from "./new-jump-type/new-jump-type.component";
import { DefaultComponent } from "./default/default.component";
import { LoginComponent } from "./login/login.component";
import { CreateUserComponent } from "./create-user/create-user.component";
import { LoginUserComponent } from "./login-user/login-user.component";
import { UserProfileComponent } from "./user-profile/user-profile.component";
import { ListOfImagesComponent } from "./list-of-images/list-of-images.component";
import { JumpInfosComponent } from "./jump-infos/jump-infos.component";
import { NewTunnelFlightComponent } from "./new-tunnel-flight/new-tunnel-flight.component";
import { ListOfTunnelFlightsComponent } from "./list-of-tunnel-flights/list-of-tunnel-flights.component";
import { DateService } from "../services/date.service";
import { AircraftService } from "../services/aircraft.service";
import { DropzoneService } from "../services/dropzone.service";
import { GearService } from "../services/gear.service";
import { JumpService } from "../services/jump.service";
import { JumpTypeService } from "../services/jump-type.service";
import { StatsService } from "../services/stats.service";
import { ServiceComm } from "../services/service-comm.service";
import { RequestCache } from "../services/request-cache.service";
import { AuthGuardService } from "../services/auth-guard.service";
import { ImageService } from "../services/image.service";
import { ConfigurationHelper } from "../services/configuration-helper";
import { ServiceCacheApi } from "../services/service-cache-api.service";
import { TunnelService } from "../services/tunnel.service";
import { TunnelFlightService } from "../services/tunnel-flight.service";
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
import { FormsModule, ReactiveFormsModule } from "@angular/forms";
import { MatAutocompleteModule } from "@angular/material/autocomplete";
import { MatButtonModule } from "@angular/material/button";
import { MatCheckboxModule } from "@angular/material/checkbox";
import { MatOptionModule, MatNativeDateModule } from "@angular/material/core";
import { MatDatepickerModule } from "@angular/material/datepicker";
import { MatFormFieldModule } from "@angular/material/form-field";
import { MatIconModule } from "@angular/material/icon";
import { MatInputModule } from "@angular/material/input";
import { MatPaginatorModule } from "@angular/material/paginator";
import { MatProgressSpinnerModule } from "@angular/material/progress-spinner";
import { MatProgressBarModule } from "@angular/material/progress-bar";
import { MatSelectModule } from "@angular/material/select";
import { MatTableModule } from "@angular/material/table";
import { MatTabsModule } from "@angular/material/tabs";
import { MatDialogModule } from "@angular/material/dialog";
import { MatCardModule } from "@angular/material/card";
import { MatRadioModule } from "@angular/material/radio";
import { MatSidenavModule } from "@angular/material/sidenav";
import { MatListModule } from "@angular/material/list";
import { MatToolbarModule } from "@angular/material/toolbar";
import { NgChartsModule } from "ng2-charts";
import { JwtAuthInterceptor } from "../interceptor/jwt-auth.interceptor";
import { ErrorInterceptor } from "../interceptor/error.interceptor";
const appRoutes: Routes = [
{
path: "",
component: DefaultComponent,
canActivate: [AuthGuardService],
},
{
path: "summary",
component: SummaryComponent,
canActivate: [AuthGuardService],
},
{
path: "jumps",
component: ListOfJumpsComponent,
canActivate: [AuthGuardService],
},
{
path: "dzs",
component: ListOfDzsComponent,
canActivate: [AuthGuardService],
},
{
path: "newjump",
component: NewJumpComponent,
canActivate: [AuthGuardService],
},
{
path: "aircrafts",
component: ListOfAircraftsComponent,
canActivate: [AuthGuardService],
},
{
path: "jumpTypes",
component: ListOfJumpTypesComponent,
canActivate: [AuthGuardService],
},
{
path: "gears",
component: ListOfGearsComponent,
canActivate: [AuthGuardService],
},
{
path: "user",
component: UserProfileComponent,
canActivate: [AuthGuardService],
},
{
path: "newTunnelFlight",
component: NewTunnelFlightComponent,
canActivate: [AuthGuardService],
},
{
path: "tunnelFlights",
component: ListOfTunnelFlightsComponent,
canActivate: [AuthGuardService],
},
{ path: "login", component: LoginComponent },
];
// AOT compilation support
export function httpTranslateLoader(http: HttpClient) {
return new TranslateHttpLoader(http);
}
// Déclaration de la fonction d'initialisation de la configuration
export function initConfig(configService: ConfigurationHelper) {
return () => configService.load(environment.env);
}
@NgModule({
declarations: [
AppComponent,
SummaryComponent,
ListOfJumpsComponent,
ListOfDzsComponent,
NewJumpComponent,
ListOfAircraftsComponent,
ListOfJumpTypesComponent,
ListOfGearsComponent,
NewAircraftComponent,
NewGearComponent,
NewDropZoneComponent,
NewJumpTypeComponent,
DefaultComponent,
LoginComponent,
CreateUserComponent,
LoginUserComponent,
UserProfileComponent,
ListOfImagesComponent,
JumpInfosComponent,
NewTunnelFlightComponent,
ListOfTunnelFlightsComponent,
],
imports: [
RouterModule.forRoot(
appRoutes,
{ enableTracing: !environment.production } // <-- debugging purposes only
// { enableTracing: !environment.production, relativeLinkResolution: 'legacy' } // <-- debugging purposes only
),
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: httpTranslateLoader,
deps: [HttpClient],
},
}),
ReactiveFormsModule,
FormsModule,
BrowserModule,
BrowserAnimationsModule,
MatPaginatorModule,
MatTableModule,
MatSelectModule,
MatOptionModule,
MatFormFieldModule,
MatCheckboxModule,
MatDatepickerModule,
MatNativeDateModule,
MatInputModule,
MatButtonModule,
MatIconModule,
MatAutocompleteModule,
MatProgressSpinnerModule,
MatProgressBarModule,
MatTabsModule,
MatDialogModule,
MatCardModule,
MatRadioModule,
MatSidenavModule,
MatListModule,
MatToolbarModule,
NgChartsModule,
],
exports: [HttpClientModule],
providers: [
TunnelService,
TunnelFlightService,
ImageService,
AircraftService,
DropzoneService,
GearService,
JumpService,
JumpTypeService,
StatsService,
ServiceComm,
DateService,
RequestCache,
ConfigurationHelper,
DatePipe,
ServiceCacheApi,
provideAppInitializer(() => {
const initializerFn = (initConfig)(inject(ConfigurationHelper));
return initializerFn();
}),
{ provide: HTTP_INTERCEPTORS, useClass: JwtAuthInterceptor, multi: true },
{ provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true },
],
bootstrap: [AppComponent],
})
export class AppModule {}

View File

@@ -0,0 +1,93 @@
import { Routes } from "@angular/router";
import { AuthGuardService } from "../services/auth-guard.service";
export const routes: Routes = [
{
path: "",
loadComponent: () =>
import("./default/default.component").then((m) => m.DefaultComponent),
canActivate: [AuthGuardService],
},
{
path: "summary",
loadComponent: () =>
import("./summary/summary.component").then((m) => m.SummaryComponent),
canActivate: [AuthGuardService],
},
{
path: "jumps",
loadComponent: () =>
import("./list-of-jumps/list-of-jumps.component").then(
(m) => m.ListOfJumpsComponent,
),
canActivate: [AuthGuardService],
},
{
path: "dzs",
loadComponent: () =>
import("./list-of-dzs/list-of-dzs.component").then(
(m) => m.ListOfDzsComponent,
),
canActivate: [AuthGuardService],
},
{
path: "newjump",
loadComponent: () =>
import("./new-jump/new-jump.component").then((m) => m.NewJumpComponent),
canActivate: [AuthGuardService],
},
{
path: "aircrafts",
loadComponent: () =>
import("./list-of-aircrafts/list-of-aircrafts.component").then(
(m) => m.ListOfAircraftsComponent,
),
canActivate: [AuthGuardService],
},
{
path: "jumpTypes",
loadComponent: () =>
import("./list-of-jump-types/list-of-jump-types.component").then(
(m) => m.ListOfJumpTypesComponent,
),
canActivate: [AuthGuardService],
},
{
path: "gears",
loadComponent: () =>
import("./list-of-gears/list-of-gears.component").then(
(m) => m.ListOfGearsComponent,
),
canActivate: [AuthGuardService],
},
{
path: "user",
loadComponent: () =>
import("./user-profile/user-profile.component").then(
(m) => m.UserProfileComponent,
),
canActivate: [AuthGuardService],
},
{
path: "newTunnelFlight",
loadComponent: () =>
import("./new-tunnel-flight/new-tunnel-flight.component").then(
(m) => m.NewTunnelFlightComponent,
),
canActivate: [AuthGuardService],
},
{
path: "tunnelFlights",
loadComponent: () =>
import("./list-of-tunnel-flights/list-of-tunnel-flights.component").then(
(m) => m.ListOfTunnelFlightsComponent,
),
canActivate: [AuthGuardService],
},
{
path: "login",
loadComponent: () =>
import("./login/login.component").then((m) => m.LoginComponent),
},
];

View File

@@ -1,72 +1,104 @@
<form [formGroup]="createForm" (ngSubmit)="onCreateSubmit()" autocomplete="off" style="padding: 10px;">
<form
focusInvalidInput
autocomplete="off"
style="padding: 10px"
(ngSubmit)="onCreateSubmit()"
[formGroup]="createForm"
>
<p>
<mat-form-field>
<mat-label>{{ 'LoginCreateUser_Firstname' | translate }}</mat-label>
<input matInput type="text" formControlName="firstname"
[ngClass]="{ 'is-invalid': submitted && formCtrls.firstname.errors }" tabindex="0" />
<mat-error *ngIf="formCtrls.firstname.hasError('required')">
{{ 'LoginCreateUser_FirstnameRequired' | translate }}
<mat-label>{{ "LoginCreateUser_Firstname" | translate }}</mat-label>
<input
type="text"
matInput
#firstname="matInput"
formControlName="firstname"
[ngClass]="{ 'is-invalid': submitted && formCtrls['firstname'].errors }"
tabindex="0"
/>
<mat-error *ngIf="formCtrls['firstname'].hasError('required')">
{{ "LoginCreateUser_FirstnameRequired" | translate }}
</mat-error>
<mat-error *ngIf="formCtrls.firstname.hasError('minlength')">
{{ 'LoginCreateUser_FirstnamePattern' | translate }}
<mat-error *ngIf="formCtrls['firstname'].hasError('minlength')">
{{ "LoginCreateUser_FirstnamePattern" | translate }}
</mat-error>
</mat-form-field>
</p>
<p>
<mat-form-field>
<mat-label>{{ 'LoginCreateUser_Lastname' | translate }}</mat-label>
<input matInput type="text" formControlName="lastname"
[ngClass]="{ 'is-invalid': submitted && formCtrls.lastname.errors }" tabindex="1" />
<mat-error *ngIf="formCtrls.lastname.hasError('required')">
{{ 'LoginCreateUser_LastnameRequired' | translate }}
<mat-label>{{ "LoginCreateUser_Lastname" | translate }}</mat-label>
<input
matInput
type="text"
formControlName="lastname"
[ngClass]="{ 'is-invalid': submitted && formCtrls['lastname'].errors }"
tabindex="1"
/>
<mat-error *ngIf="formCtrls['lastname'].hasError('required')">
{{ "LoginCreateUser_LastnameRequired" | translate }}
</mat-error>
<mat-error *ngIf="formCtrls.lastname.hasError('minlength')">
{{ 'LoginCreateUser_LastnamePattern' | translate }}
<mat-error *ngIf="formCtrls['lastname'].hasError('minlength')">
{{ "LoginCreateUser_LastnamePattern" | translate }}
</mat-error>
</mat-form-field>
</p>
<p>
<mat-form-field>
<mat-label>{{ 'LoginCreateUser_Email' | translate }}</mat-label>
<input matInput type="email" formControlName="email"
[ngClass]="{ 'is-invalid': submitted && formCtrls.email.errors }" tabindex="3" />
<mat-error *ngIf="formCtrls.email.hasError('required')">
{{ 'LoginCreateUser_EmailRequired' | translate }}
<mat-label>{{ "LoginCreateUser_Email" | translate }}</mat-label>
<input
matInput
type="email"
formControlName="email"
[ngClass]="{ 'is-invalid': submitted && formCtrls['email'].errors }"
tabindex="3"
/>
<mat-error *ngIf="formCtrls['email'].hasError('required')">
{{ "LoginCreateUser_EmailRequired" | translate }}
</mat-error>
<mat-error *ngIf="formCtrls.email.hasError('email')">
{{ 'LoginCreateUser_EmailPattern' | translate }}
<mat-error *ngIf="formCtrls['email'].hasError('email')">
{{ "LoginCreateUser_EmailPattern" | translate }}
</mat-error>
</mat-form-field>
</p>
<p>
<mat-form-field>
<mat-label>{{ 'LoginCreateUser_Username' | translate }}</mat-label>
<input matInput type="text" formControlName="username"
[ngClass]="{ 'is-invalid': submitted && formCtrls.username.errors }" tabindex="4" />
<mat-error *ngIf="formCtrls.username.hasError('required')">
{{ 'LoginCreateUser_UsernameRequired' | translate }}
<mat-label>{{ "LoginCreateUser_Username" | translate }}</mat-label>
<input
matInput
type="text"
formControlName="username"
[ngClass]="{ 'is-invalid': submitted && formCtrls['username'].errors }"
tabindex="4"
/>
<mat-error *ngIf="formCtrls['username'].hasError('required')">
{{ "LoginCreateUser_UsernameRequired" | translate }}
</mat-error>
<mat-error *ngIf="formCtrls.username.hasError('minlength')">
{{ 'LoginCreateUser_UsernamePattern' | translate }}
<mat-error *ngIf="formCtrls['username'].hasError('minlength')">
{{ "LoginCreateUser_UsernamePattern" | translate }}
</mat-error>
</mat-form-field>
</p>
<p>
<mat-form-field>
<mat-label>{{ 'LoginCreateUser_Password' | translate }}</mat-label>
<input matInput type="password" formControlName="password"
[ngClass]="{ 'is-invalid': submitted && formCtrls.password.errors }" tabindex="5" />
<mat-error *ngIf="formCtrls.password.hasError('required')">
{{ 'LoginCreateUser_PasswordRequired' | translate }}
<mat-label>{{ "LoginCreateUser_Password" | translate }}</mat-label>
<input
matInput
type="password"
formControlName="password"
[ngClass]="{ 'is-invalid': submitted && formCtrls['password'].errors }"
tabindex="5"
/>
<mat-error *ngIf="formCtrls['password'].hasError('required')">
{{ "LoginCreateUser_PasswordRequired" | translate }}
</mat-error>
<mat-error *ngIf="formCtrls.password.hasError('pattern')">
{{ 'LoginCreateUser_PasswordPattern' | translate }}
<mat-error *ngIf="formCtrls['password'].hasError('pattern')">
{{ "LoginCreateUser_PasswordPattern" | translate }}
</mat-error>
</mat-form-field>
</p>
<button [disabled]="!createForm.valid" mat-raised-button color="accent">
{{ 'LoginCreateUser_BtnLogin' | translate }}
{{ "LoginCreateUser_BtnLogin" | translate }}
</button>
<div *ngIf="error" class="alert alert-danger mt-3 mb-0">{{error}}</div>
<div *ngIf="error" class="alert alert-danger mt-3 mb-0">{{ error }}</div>
</form>

View File

@@ -1,7 +1,20 @@
import { Component, OnInit } from "@angular/core";
import { Component, OnInit, ViewChild } from "@angular/core";
import { Router, ActivatedRoute } from "@angular/router";
import { FormBuilder, FormGroup, Validators } from "@angular/forms";
import { TranslateService } from "@ngx-translate/core";
import {
FormBuilder,
FormGroup,
ReactiveFormsModule,
Validators,
} from "@angular/forms";
import {
TranslateModule,
TranslatePipe,
TranslateService,
} from "@ngx-translate/core";
import { CommonModule } from "@angular/common";
import { MatFormFieldModule } from "@angular/material/form-field";
import { MatInputModule } from "@angular/material/input";
import { MatButtonModule } from "@angular/material/button";
import { first } from "rxjs/operators";
@@ -9,23 +22,33 @@ import { AuthenticationService } from "../../services/authentication.service";
import { User } from "../../models/user";
@Component({
selector: "app-create-user",
templateUrl: "./create-user.component.html",
styleUrls: ["./create-user.component.css"],
standalone: false
selector: "app-create-user",
templateUrl: "./create-user.component.html",
styleUrls: ["./create-user.component.css"],
imports: [
CommonModule,
MatFormFieldModule,
ReactiveFormsModule,
MatInputModule,
MatButtonModule,
TranslateModule,
TranslatePipe,
],
})
export class CreateUserComponent implements OnInit {
createForm: FormGroup;
invalidForm = true;
submitted = false;
returnUrl: string;
error = "";
public createForm: FormGroup;
public invalidForm = true;
public submitted = false;
public returnUrl: string;
public error: string = "";
constructor(private formBuilder: FormBuilder,
private route: ActivatedRoute,
private router: Router,
private authenticationService: AuthenticationService,
private translateService: TranslateService) {
constructor(
private formBuilder: FormBuilder,
private route: ActivatedRoute,
private router: Router,
private authenticationService: AuthenticationService,
private translateService: TranslateService,
) {
// redirect to home if already logged in
if (this.authenticationService.currentUserValue) {
this.router.navigate(["/"]);
@@ -35,16 +58,21 @@ export class CreateUserComponent implements OnInit {
ngOnInit() {
this.createForm = this.formBuilder.group(
{
firstname: ["", [Validators.required, Validators.minLength(3)]],
lastname: ["", [Validators.required, Validators.minLength(3)]],
email: ["", [Validators.required, Validators.email]],
username: ["", [Validators.required, Validators.minLength(3)]],
password: [
"",
[Validators.required, Validators.pattern("^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[@$!%*#?&\-_|]).{8,}$")]
[
Validators.required,
Validators.pattern(
"^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[@$!%*#?&-_|]).{8,}$",
),
],
],
firstname: ["", [Validators.required, Validators.minLength(3)]],
lastname: ["", [Validators.required, Validators.minLength(3)]],
email: ["", [Validators.required, Validators.email]]
},
{ updateOn: "blur" }
{ updateOn: "blur" },
);
// get return url from route parameters or default to '/'
@@ -58,30 +86,26 @@ export class CreateUserComponent implements OnInit {
onCreateSubmit() {
this.invalidForm = false;
this.submitted = true;
if (this.createForm.invalid) {
this.invalidForm = true;
return;
}
let createUser = new User();
createUser.login = this.formCtrls.username.value;
createUser.password = this.formCtrls.password.value;
createUser.firstName = this.formCtrls.firstname.value;
createUser.lastName = this.formCtrls.lastname.value;
createUser.email = this.formCtrls.email.value;
createUser.language = this.translateService.currentLang;
this.authenticationService.create(createUser)
.pipe(first())
.subscribe(
data => {
this.router.navigate([this.returnUrl]);
},
error => {
this.error = error;
this.invalidForm = false;
}
);
createUser.login = this.formCtrls["username"].value;
createUser.password = this.formCtrls["password"].value;
createUser.firstName = this.formCtrls["firstname"].value;
createUser.lastName = this.formCtrls["lastname"].value;
createUser.email = this.formCtrls["email"].value;
createUser.language = this.translateService.getCurrentLang();
this.authenticationService
.create(createUser)
.pipe(first())
.subscribe({
complete: () => this.router.navigate([this.returnUrl]),
error: (error) => {
this.error = error.message;
this.invalidForm = false;
},
});
}
}

View File

@@ -1,23 +1,62 @@
<div class="content">
<p>
<a class="nostyle" routerLink="/summary" routerLinkActive="active" skipLocationChange>
<mat-icon aria-hidden="false" aria-label="Summary" style="width: 128px; height:128px; font-size: 128px;">timeline</mat-icon>
</a>
</p>
<p>
<a class="nostyle" routerLink="/newjump" routerLinkActive="active" skipLocationChange>
<mat-icon aria-hidden="false" aria-label="Add jumps" style="width: 128px; height:128px; font-size: 128px;">add_circle</mat-icon>
</a>
</p>
<p>
<a class="nostyle" routerLink="/jumps" routerLinkActive="active" skipLocationChange>
<mat-icon aria-hidden="false" aria-label="List of jumps" style="width: 128px; height:128px; font-size: 128px;">list_alt</mat-icon>
</a>
</p>
<p>
<a class="nostyle" routerLink="/tunnelFlights" routerLinkActive="active" skipLocationChange>
<img src="assets/img/tunnel.jpg" alt="Tunnel flights" style="width: 128px; height:128px; font-size: 128px;" />
</a>
</p>
<p>
<a
class="nostyle"
routerLink="/summary"
routerLinkActive="active"
skipLocationChange
>
<mat-icon
aria-hidden="false"
aria-label="Summary"
style="width: 128px; height: 128px; font-size: 128px"
svgIcon="summary"
></mat-icon>
</a>
</p>
<p>
<a
class="nostyle"
routerLink="/newjump"
routerLinkActive="active"
skipLocationChange
>
<mat-icon
aria-hidden="false"
aria-label="Add jumps"
style="width: 128px; height: 128px; font-size: 128px"
svgIcon="add"
></mat-icon>
</a>
</p>
<p>
<a
class="nostyle"
routerLink="/jumps"
routerLinkActive="active"
skipLocationChange
>
<mat-icon
aria-hidden="false"
aria-label="List of jumps"
style="width: 128px; height: 128px; font-size: 128px"
svgIcon="list"
></mat-icon>
</a>
</p>
<p>
<a
class="nostyle"
routerLink="/tunnelFlights"
routerLinkActive="active"
skipLocationChange
>
<mat-icon
aria-hidden="false"
aria-label="List of tunnel flights"
style="width: 128px; height: 128px; font-size: 128px"
svgIcon="wind"
></mat-icon>
</a>
</p>
</div>

View File

@@ -1,6 +1,8 @@
import { Component, OnInit } from "@angular/core";
import { TranslateService } from "@ngx-translate/core";
import { RouterLink } from "@angular/router";
import { TranslateModule, TranslateService } from "@ngx-translate/core";
import { Observable, forkJoin } from "rxjs";
import { MatIconModule } from "@angular/material/icon";
import { AircraftService } from "../../services/aircraft.service";
import { AuthenticationService } from "../../services/authentication.service";
@@ -10,27 +12,31 @@ import { JumpTypeService } from "../../services/jump-type.service";
import { ServiceComm } from "../../services/service-comm.service";
@Component({
selector: "app-default",
templateUrl: "./default.component.html",
styleUrls: ["./default.component.css"],
standalone: false
selector: "app-default",
templateUrl: "./default.component.html",
styleUrls: ["./default.component.css"],
imports: [TranslateModule, MatIconModule, RouterLink],
})
export class DefaultComponent implements OnInit {
constructor(private serviceComm: ServiceComm,
private translateService: TranslateService,
private authenticationService: AuthenticationService,
private serviceApiAircraft : AircraftService,
private serviceApiJumpType : JumpTypeService,
private serviceApiDropzone : DropzoneService,
private serviceApiGear : GearService) {}
constructor(
private serviceComm: ServiceComm,
private translateService: TranslateService,
private authenticationService: AuthenticationService,
private serviceApiAircraft: AircraftService,
private serviceApiJumpType: JumpTypeService,
private serviceApiDropzone: DropzoneService,
private serviceApiGear: GearService,
) {}
ngOnInit() {
this.authenticationService.alwaysLogin();
this.putToCacheRefDatas().subscribe(() => { console.log("Push to cache the referentiel datas"); });
this.putToCacheRefDatas().subscribe(() => {
console.log("Push to cache the referentiel datas");
});
this.updateTitle();
this.serviceComm.forceTranslateTitle.subscribe((data)=> {
if (data === true){
this.serviceComm.forceTranslateTitle.subscribe((data) => {
if (data === true) {
this.updateTitle();
}
});
@@ -46,8 +52,8 @@ export class DefaultComponent implements OnInit {
}
private updateTitle() {
this.translateService.get("Default_Title").subscribe(
data => { this.serviceComm.updatedComponentTitle(data); }
);
this.translateService.get("Default_Title").subscribe((data) => {
this.serviceComm.updatedComponentTitle(data);
});
}
}
}

View File

@@ -1,11 +1,34 @@
<form (ngSubmit)="updateJump()">
<p><span>Gear : {{jump.gear.name}} ({{jump.gear.mainCanopy}})</span></p>
<p><mat-checkbox [(ngModel)]="jump.isSpecial" name="isSpecial" labelPosition="before">Special jump</mat-checkbox></p>
<p><mat-checkbox [(ngModel)]="jump.withCutaway" name="withCutaway" labelPosition="before">Cutaway</mat-checkbox></p>
<mat-form-field>
<textarea matInput placeholder="Comments" name="comments" [(ngModel)]="jump.notes" name="comments" type="text"></textarea>
</mat-form-field>
<p>
<span>Gear : {{ jump.gear.name }} ({{ jump.gear.mainCanopy }})</span>
</p>
<p>
<mat-checkbox
[(ngModel)]="jump.isSpecial"
name="isSpecial"
labelPosition="before"
>Special jump</mat-checkbox
>
</p>
<p>
<mat-checkbox
[(ngModel)]="jump.withCutaway"
name="withCutaway"
labelPosition="before"
>Cutaway</mat-checkbox
>
</p>
<mat-form-field>
<textarea
matInput
placeholder="Comments"
name="comments"
[(ngModel)]="jump.notes"
name="comments"
type="text"
></textarea>
</mat-form-field>
<br />
<button mat-raised-button color="accent" *ngIf="editMode">Update</button>
</form>
<br />
<button mat-raised-button color="accent" *ngIf="editMode">Update</button>
</form>

View File

@@ -1,24 +1,43 @@
import { Component, Inject, OnInit } from '@angular/core';
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { Component, Inject, OnInit } from "@angular/core";
import { CommonModule } from "@angular/common";
import { MAT_DIALOG_DATA } from "@angular/material/dialog";
import { TranslateModule } from "@ngx-translate/core";
import { MatCheckboxModule } from "@angular/material/checkbox";
import { MatFormFieldModule } from "@angular/material/form-field";
import { FormsModule, ReactiveFormsModule } from "@angular/forms";
import { MatInputModule } from "@angular/material/input";
import { MatButtonModule } from "@angular/material/button";
import { AddAction } from '../../models/add-action.enum';
import { JumpResp } from '../../models/jump';
import { JumpService } from '../../services/jump.service';
import { ServiceComm } from '../../services/service-comm.service';
import { AddAction } from "../../models/add-action.enum";
import { JumpResp } from "../../models/jump";
import { JumpService } from "../../services/jump.service";
import { ServiceComm } from "../../services/service-comm.service";
@Component({
selector: 'app-jump-infos',
templateUrl: './jump-infos.component.html',
styleUrls: ['./jump-infos.component.css'],
standalone: false
selector: "app-jump-infos",
templateUrl: "./jump-infos.component.html",
styleUrls: ["./jump-infos.component.css"],
imports: [
TranslateModule,
CommonModule,
FormsModule,
MatCheckboxModule,
MatFormFieldModule,
ReactiveFormsModule,
MatInputModule,
MatButtonModule,
],
})
export class JumpInfosComponent implements OnInit {
public editMode: boolean;
public jump: JumpResp
public jump: JumpResp;
constructor(@Inject(MAT_DIALOG_DATA) public data: any,
private serviceJump: JumpService,
private serviceComm: ServiceComm) {
constructor(
@Inject(MAT_DIALOG_DATA) public data: any,
private serviceJump: JumpService,
private serviceComm: ServiceComm
) {
this.jump = new JumpResp(data.jump);
this.editMode = data.editMode;
}
@@ -26,12 +45,15 @@ export class JumpInfosComponent implements OnInit {
ngOnInit(): void {}
public updateJump() {
this.serviceJump.updateJump(this.jump.id,
this.jump.isSpecial,
this.jump.withCutaway,
this.jump.notes)
.subscribe(() => {
this.serviceComm.refreshData(AddAction.Jump);
});
this.serviceJump
.updateJump(
this.jump.id,
this.jump.isSpecial,
this.jump.withCutaway,
this.jump.notes
)
.subscribe(() => {
this.serviceComm.refreshData(AddAction.Jump);
});
}
}

View File

@@ -1,25 +1,44 @@
<div class="content">
<div *ngIf="dataSourceTable != null else loading">
<button mat-raised-button color="accent" (click)="openDialogToAdd()" *ngIf="isUserAdmin == true">{{ 'List_Aircrafts_Add' | translate }}</button>
<div *ngIf="dataSourceTable != null; else loading">
<button
mat-raised-button
color="accent"
(click)="openDialogToAdd()"
*ngIf="isUserAdmin == true"
>
{{ "ListAircrafts_Add" | translate }}
</button>
<table mat-table [dataSource]="dataSourceTable">
<ng-container matColumnDef="id">
<th mat-header-cell *matHeaderCellDef>{{ 'List_Aircrafts_Header_Id' | translate }}</th>
<td mat-cell *matCellDef="let element">{{element.id}}</td>
<th mat-header-cell *matHeaderCellDef>
{{ "ListAircrafts_Header_Id" | translate }}
</th>
<td mat-cell *matCellDef="let element">{{ element.id }}</td>
</ng-container>
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef>{{ 'List_Aircrafts_Header_Name' | translate }}</th>
<td mat-cell *matCellDef="let element">{{element.name}}</td>
<th mat-header-cell *matHeaderCellDef>
{{ "ListAircrafts_Header_Name" | translate }}
</th>
<td mat-cell *matCellDef="let element">{{ element.name }}</td>
</ng-container>
<ng-container matColumnDef="imageData">
<th mat-header-cell *matHeaderCellDef>{{ 'List_Aircrafts_Header_Image' | translate }}</th>
<td mat-cell *matCellDef="let element"><img src="{{element.imageData}}" alt="No image" style="width: 128px;"></td>
<th mat-header-cell *matHeaderCellDef>
{{ "ListAircrafts_Header_Image" | translate }}
</th>
<td mat-cell *matCellDef="let element">
<img
src="{{ element.imageData }}"
alt="No image"
style="width: 128px"
/>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
</table>
</div>
<mat-paginator [length]="resultsLength" [pageSize]="10"></mat-paginator>

View File

@@ -1,56 +1,69 @@
import { Component, OnInit, ViewChild } from '@angular/core';
import { MatPaginator } from '@angular/material/paginator';
import { MatTableDataSource } from '@angular/material/table';
import { MatDialog } from '@angular/material/dialog';
import { TranslateService } from '@ngx-translate/core';
import { Component, OnInit, ViewChild } from "@angular/core";
import { MatPaginator, MatPaginatorModule } from "@angular/material/paginator";
import { MatTableDataSource, MatTableModule } from "@angular/material/table";
import { MatDialog } from "@angular/material/dialog";
import { TranslateModule, TranslateService } from "@ngx-translate/core";
import { CommonModule } from "@angular/common";
import { MatProgressSpinnerModule } from "@angular/material/progress-spinner";
import { MatButtonModule } from "@angular/material/button";
import { AircraftService } from '../../services/aircraft.service';
import { ServiceComm } from '../../services/service-comm.service';
import { AuthenticationService } from '../../services/authentication.service';
import { NewAircraftComponent } from '../new-aircraft/new-aircraft.component';
import { AddAction } from '../../models/add-action.enum';
import { AircraftResp } from '../../models/aircraft';
import { AircraftService } from "../../services/aircraft.service";
import { ServiceComm } from "../../services/service-comm.service";
import { AuthenticationService } from "../../services/authentication.service";
import { NewAircraftComponent } from "../new-aircraft/new-aircraft.component";
import { AddAction } from "../../models/add-action.enum";
import { AircraftResp } from "../../models/aircraft";
@Component({
selector: 'app-list-of-aircrafts',
templateUrl: './list-of-aircrafts.component.html',
styleUrls: ['./list-of-aircrafts.component.css'],
standalone: false
selector: "app-list-of-aircrafts",
templateUrl: "./list-of-aircrafts.component.html",
styleUrls: ["./list-of-aircrafts.component.css"],
imports: [
TranslateModule,
CommonModule,
MatPaginatorModule,
MatProgressSpinnerModule,
MatTableModule,
MatButtonModule,
],
})
export class ListOfAircraftsComponent implements OnInit {
public displayedColumns: Array<string> = ['name', 'imageData'];
public displayedColumns: Array<string> = ["name", "imageData"];
public dataSourceTable: MatTableDataSource<AircraftResp>;
public resultsLength = 0;
public isUserAdmin: boolean;
@ViewChild(MatPaginator, { static: true }) paginator: MatPaginator;
constructor(private serviceApi: AircraftService,
private serviceComm: ServiceComm,
private authenticationService: AuthenticationService,
public dialog: MatDialog,
private translateService: TranslateService) {
this.isUserAdmin = this.authenticationService.currentUserValue.roles === "admin";
constructor(
private serviceApi: AircraftService,
private serviceComm: ServiceComm,
private authenticationService: AuthenticationService,
public dialog: MatDialog,
private translateService: TranslateService
) {
this.isUserAdmin =
this.authenticationService.currentUserValue.roles === "admin";
}
ngOnInit() {
this.serviceComm.refreshRequest.subscribe(action => {
this.serviceComm.refreshRequest.subscribe((action) => {
if (action === AddAction.Aircraft) {
this.dialog.closeAll();
this.getListOfAircrafts();
}
});
this.serviceComm.forceTranslateTitle.subscribe((data)=> {
if (data === true){
this.serviceComm.forceTranslateTitle.subscribe((data) => {
if (data === true) {
this.updateTitle();
}
});
this.updateTitle();
this.getListOfAircrafts();
}
private getListOfAircrafts() {
this.serviceApi.getListOfAircrafts().subscribe(data => {
this.serviceApi.getListOfAircrafts().subscribe((data) => {
setTimeout(() => {
data.sort((a, b) => a.name.localeCompare(b.name));
this.dataSourceTable = new MatTableDataSource<AircraftResp>(data);
@@ -65,8 +78,8 @@ export class ListOfAircraftsComponent implements OnInit {
}
private updateTitle() {
this.translateService.get("ListAircrafts_Title").subscribe(
data => { this.serviceComm.updatedComponentTitle(data); }
);
this.translateService.get("ListAircrafts_Title").subscribe((data) => {
this.serviceComm.updatedComponentTitle(data);
});
}
}

View File

@@ -1,59 +1,113 @@
<div class="content">
<div *ngIf="dataSourceTable != null else loading">
<button mat-raised-button color="accent" (click)="openDialogToAdd()" *ngIf="isUserAdmin == true">{{ 'List_Dz_Add' | translate }}</button>
<div *ngIf="dataSourceTable != null; else loading">
<button
mat-raised-button
color="accent"
(click)="openDialogToAdd()"
*ngIf="isUserAdmin == true"
>
{{ "ListDz_Add" | translate }}
</button>
<mat-form-field>
<mat-label>{{ 'List_Dz_Filter' | translate }}</mat-label>
<input matInput (keyup)="applyFilter($event)" placeholder="{{ 'List_Dz_Filter_PlaceHolder' | translate }}" #input>
<mat-label>{{ "ListDz_Filter" | translate }}</mat-label>
<input
matInput
(keyup)="applyFilter($event)"
placeholder="{{ 'ListDz_Filter_PlaceHolder' | translate }}"
#input
/>
</mat-form-field>
<table mat-table [dataSource]="dataSourceTable">
<ng-container matColumnDef="isfavorite">
<th mat-header-cell *matHeaderCellDef style="min-width: 144px;"></th>
<td mat-cell *matCellDef="let element" style="text-align: left;">
<mat-icon aria-hidden="false" aria-label="Favorite" *ngIf="element.isFavorite === true"
(click)="removeToFavorite(element)" color="primary" style="cursor: pointer;">favorite</mat-icon>
<mat-icon aria-hidden="false" aria-label="Not favorite" *ngIf="element.isFavorite === false"
(click)="setToFavorite(element)" style="cursor: pointer;">favorite_border</mat-icon>
<a href='http://{{element.website}}' target="_blank">
<mat-icon aria-hidden="false" aria-label="URL to the DZ website" style="color: white;">link</mat-icon>
<th mat-header-cell *matHeaderCellDef style="min-width: 144px"></th>
<td mat-cell *matCellDef="let element" style="text-align: left">
<mat-icon
aria-hidden="false"
aria-label="Favorite"
*ngIf="element.isFavorite === true"
(click)="removeToFavorite(element)"
color="primary"
style="cursor: pointer"
svgIcon="favorite"
></mat-icon>
<mat-icon
aria-hidden="false"
aria-label="Not favorite"
*ngIf="element.isFavorite === false"
(click)="setToFavorite(element)"
style="cursor: pointer"
svgIcon="not_favorite"
></mat-icon>
<a href="http://{{ element.website }}" target="_blank">
<mat-icon
aria-hidden="false"
aria-label="URL to the DZ website"
style="color: white"
svgIcon="link"
></mat-icon>
</a>
<a href='https://www.openstreetmap.org/?mlat={{element.latitude}}&mlon={{element.longitude}}#map=14/{{element.latitude}}/{{element.longitude}}'
target="_blank">
<mat-icon aria-hidden="false" aria-label="Location of the DZ" style="color: white;">map</mat-icon>
<a
href="https://www.openstreetmap.org/?mlat={{
element.latitude
}}&mlon={{ element.longitude }}#map=14/{{ element.latitude }}/{{
element.longitude
}}"
target="_blank"
>
<mat-icon
aria-hidden="false"
aria-label="Location of the DZ"
style="color: white"
svgIcon="map"
></mat-icon>
</a>
<a href="mailto:{{element.email}}" *ngIf="element.email">
<mat-icon aria-hidden="false" aria-label="Contact mail of the DZ" style="color: white;">mail_outline</mat-icon>
<a href="mailto:{{ element.email }}" *ngIf="element.email">
<mat-icon
aria-hidden="false"
aria-label="Contact mail of the DZ"
style="color: white"
svgIcon="mail"
></mat-icon>
</a>
</td>
</ng-container>
<ng-container matColumnDef="id">
<th mat-header-cell *matHeaderCellDef>{{ 'List_Dz_Header_ID' | translate }}</th>
<td mat-cell *matCellDef="let element">{{element.id}}</td>
<th mat-header-cell *matHeaderCellDef>
{{ "ListDz_Header_ID" | translate }}
</th>
<td mat-cell *matCellDef="let element">{{ element.id }}</td>
</ng-container>
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef>{{ 'List_Dz_Header_Name' | translate }}</th>
<th mat-header-cell *matHeaderCellDef>
{{ "ListDz_Header_Name" | translate }}
</th>
<td mat-cell *matCellDef="let element">
<span class="spanWithBreakWord" [innerHTML]="element.name"></span>
</td>
</ng-container>
<ng-container matColumnDef="address">
<th mat-header-cell *matHeaderCellDef>{{ 'List_Dz_Header_Address' | translate }}</th>
<th mat-header-cell *matHeaderCellDef>
{{ "ListDz_Header_Address" | translate }}
</th>
<td mat-cell *matCellDef="let element">
<span class="spanWithBreakWord" [innerHTML]="element.address"></span>
</td>
</ng-container>
<ng-container matColumnDef="type">
<th mat-header-cell *matHeaderCellDef>{{ 'List_Dz_Header_Type' | translate }}</th>
<td mat-cell *matCellDef="let element">{{element.type}}</td>
<th mat-header-cell *matHeaderCellDef>
{{ "ListDz_Header_Type" | translate }}
</th>
<td mat-cell *matCellDef="let element">{{ element.type }}</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
</table>
</div>
<mat-paginator [length]="resultsLength" [pageSize]="20"></mat-paginator>

View File

@@ -1,40 +1,61 @@
import { Component, OnInit, ViewChild } from '@angular/core';
import { MatPaginator } from '@angular/material/paginator';
import { MatTableDataSource } from '@angular/material/table';
import { MatDialog } from '@angular/material/dialog';
import { TranslateService } from '@ngx-translate/core';
import { Component, OnInit, ViewChild } from "@angular/core";
import { MatPaginator, MatPaginatorModule } from "@angular/material/paginator";
import { MatTableDataSource, MatTableModule } from "@angular/material/table";
import { MatDialog } from "@angular/material/dialog";
import { TranslateModule, TranslateService } from "@ngx-translate/core";
import { CommonModule } from "@angular/common";
import { MatIconModule } from "@angular/material/icon";
import { MatProgressSpinnerModule } from "@angular/material/progress-spinner";
import { MatFormFieldModule } from "@angular/material/form-field";
import { MatButtonModule } from "@angular/material/button";
import { MatInputModule } from "@angular/material/input";
import { ReactiveFormsModule } from "@angular/forms";
import { AddAction } from '../../models/add-action.enum';
import { DropZoneResp } from '../../models/dropzone';
import { DropzoneService } from '../../services/dropzone.service';
import { ServiceComm } from '../../services/service-comm.service';
import { AuthenticationService } from '../../services/authentication.service';
import { NewDropZoneComponent } from '../new-drop-zone/new-drop-zone.component';
import { AddAction } from "../../models/add-action.enum";
import { DropZoneResp } from "../../models/dropzone";
import { DropzoneService } from "../../services/dropzone.service";
import { ServiceComm } from "../../services/service-comm.service";
import { AuthenticationService } from "../../services/authentication.service";
import { NewDropZoneComponent } from "../new-drop-zone/new-drop-zone.component";
@Component({
selector: 'app-list-of-dzs',
templateUrl: './list-of-dzs.component.html',
styleUrls: ['./list-of-dzs.component.css'],
standalone: false
selector: "app-list-of-dzs",
templateUrl: "./list-of-dzs.component.html",
styleUrls: ["./list-of-dzs.component.css"],
imports: [
TranslateModule,
CommonModule,
MatIconModule,
MatPaginatorModule,
MatProgressSpinnerModule,
MatTableModule,
MatFormFieldModule,
ReactiveFormsModule,
MatInputModule,
MatButtonModule,
],
})
export class ListOfDzsComponent implements OnInit {
public displayedColumns: Array<string> = [
'isfavorite',
'name',
'address',
'type',
"isfavorite",
"name",
"address",
"type",
];
public dataSourceTable: MatTableDataSource<DropZoneResp>;
public isUserAdmin: boolean;
public resultsLength = 0;
@ViewChild(MatPaginator, { static: true }) paginator: MatPaginator;
constructor(private serviceApi: DropzoneService,
private serviceComm: ServiceComm,
private authenticationService: AuthenticationService,
public dialog: MatDialog,
private translateService: TranslateService) {
this.isUserAdmin = this.authenticationService.currentUserValue.roles === "admin";
constructor(
private serviceApi: DropzoneService,
private serviceComm: ServiceComm,
private authenticationService: AuthenticationService,
public dialog: MatDialog,
private translateService: TranslateService
) {
this.isUserAdmin =
this.authenticationService.currentUserValue.roles === "admin";
}
ngOnInit() {
@@ -44,32 +65,35 @@ export class ListOfDzsComponent implements OnInit {
this.getListOfDropZones();
}
});
this.serviceComm.forceTranslateTitle.subscribe((data)=> {
if (data === true){
this.serviceComm.forceTranslateTitle.subscribe((data) => {
if (data === true) {
this.updateTitle();
}
});
this.updateTitle();
this.getListOfDropZones();
}
private getListOfDropZones() {
this.serviceApi.getListOfDropZones()
.subscribe((data) => {
setTimeout(() => {
data.sort((a, b) => (b.isFavorite ? 1 : 0) - (a.isFavorite ? 1 : 0) || a.name.localeCompare(b.name));
this.dataSourceTable = new MatTableDataSource<DropZoneResp>(data);
this.dataSourceTable.paginator = this.paginator;
this.resultsLength = data.length;
}, 500);
});
this.serviceApi.getListOfDropZones().subscribe((data) => {
setTimeout(() => {
data.sort(
(a, b) =>
(b.isFavorite ? 1 : 0) - (a.isFavorite ? 1 : 0) ||
a.name.localeCompare(b.name)
);
this.dataSourceTable = new MatTableDataSource<DropZoneResp>(data);
this.dataSourceTable.paginator = this.paginator;
this.resultsLength = data.length;
}, 500);
});
}
public setToFavorite(dropzone: DropZoneResp) {
dropzone.isFavorite = true;
this.serviceApi.setFavoriteDropZone(dropzone);
const data = this.dataSourceTable.data;
data.sort((a, b) => (b.isFavorite ? 1 : 0) - (a.isFavorite ? 1 : 0));
this.dataSourceTable = new MatTableDataSource<DropZoneResp>(data);
@@ -88,8 +112,8 @@ export class ListOfDzsComponent implements OnInit {
openDialogToAdd() {
this.dialog.open(NewDropZoneComponent, {
height: '400px',
width: '600px',
height: "400px",
width: "600px",
});
}
@@ -99,8 +123,8 @@ export class ListOfDzsComponent implements OnInit {
}
private updateTitle() {
this.translateService.get("ListDz_Title").subscribe(
data => { this.serviceComm.updatedComponentTitle(data); }
);
this.translateService.get("ListDz_Title").subscribe((data) => {
this.serviceComm.updatedComponentTitle(data);
});
}
}

View File

@@ -1,45 +1,63 @@
<div class="content">
<div *ngIf="dataSourceTable != null else loading">
<button mat-raised-button color="accent" (click)="openDialogToAdd()">{{ 'List_Gears_Add' | translate }}</button>
<div *ngIf="dataSourceTable != null; else loading">
<button mat-raised-button color="accent" (click)="openDialogToAdd()">
{{ "ListGears_Add" | translate }}
</button>
<table mat-table [dataSource]="dataSourceTable">
<ng-container matColumnDef="id">
<th mat-header-cell *matHeaderCellDef>{{ 'List_Gears_Header_Id' | translate }}</th>
<td mat-cell *matCellDef="let element">{{element.id}}</td>
<th mat-header-cell *matHeaderCellDef>
{{ "ListGears_Header_Id" | translate }}
</th>
<td mat-cell *matCellDef="let element">{{ element.id }}</td>
</ng-container>
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef style="min-width: 130px;">{{ 'List_Gears_Header_Name' | translate }}</th>
<td mat-cell *matCellDef="let element">{{element.name}}</td>
<th mat-header-cell *matHeaderCellDef style="min-width: 130px">
{{ "ListGears_Header_Name" | translate }}
</th>
<td mat-cell *matCellDef="let element">{{ element.name }}</td>
</ng-container>
<ng-container matColumnDef="manufacturer">
<th mat-header-cell *matHeaderCellDef>{{ 'List_Gears_Header_Manufacturer' | translate }}</th>
<td mat-cell *matCellDef="let element">{{element.manufacturer}}</td>
<th mat-header-cell *matHeaderCellDef>
{{ "ListGears_Header_Manufacturer" | translate }}
</th>
<td mat-cell *matCellDef="let element">{{ element.manufacturer }}</td>
</ng-container>
<ng-container matColumnDef="maxSize">
<th mat-header-cell *matHeaderCellDef style="min-width: 90px;">{{ 'List_Gears_Header_CanopySize' | translate }}</th>
<td mat-cell *matCellDef="let element">{{element.minSize}} - {{element.maxSize}}</td>
<th mat-header-cell *matHeaderCellDef style="min-width: 90px">
{{ "ListGears_Header_CanopySize" | translate }}
</th>
<td mat-cell *matCellDef="let element">
{{ element.minSize }} - {{ element.maxSize }}
</td>
</ng-container>
<ng-container matColumnDef="aad">
<th mat-header-cell *matHeaderCellDef>{{ 'List_Gears_Header_Aad' | translate }}</th>
<td mat-cell *matCellDef="let element">{{element.aad}}</td>
<th mat-header-cell *matHeaderCellDef>
{{ "ListGears_Header_Aad" | translate }}
</th>
<td mat-cell *matCellDef="let element">{{ element.aad }}</td>
</ng-container>
<ng-container matColumnDef="mainCanopy">
<th mat-header-cell *matHeaderCellDef>{{ 'List_Gears_Header_Main' | translate }}</th>
<td mat-cell *matCellDef="let element">{{element.mainCanopy}}</td>
<th mat-header-cell *matHeaderCellDef>
{{ "ListGears_Header_Main" | translate }}
</th>
<td mat-cell *matCellDef="let element">{{ element.mainCanopy }}</td>
</ng-container>
<ng-container matColumnDef="reserveCanopy">
<th mat-header-cell *matHeaderCellDef>{{ 'List_Gears_Header_Reserve' | translate }}</th>
<td mat-cell *matCellDef="let element">{{element.reserveCanopy}}</td>
<th mat-header-cell *matHeaderCellDef>
{{ "ListGears_Header_Reserve" | translate }}
</th>
<td mat-cell *matCellDef="let element">{{ element.reserveCanopy }}</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
</table>
</div>
<mat-paginator [length]="resultsLength" [pageSize]="10"></mat-paginator>

View File

@@ -1,8 +1,11 @@
import { Component, OnInit, ViewChild } from "@angular/core";
import { MatPaginator } from "@angular/material/paginator";
import { MatTableDataSource } from "@angular/material/table";
import { MatPaginator, MatPaginatorModule } from "@angular/material/paginator";
import { MatTableDataSource, MatTableModule } from "@angular/material/table";
import { MatDialog } from "@angular/material/dialog";
import { TranslateService } from '@ngx-translate/core';
import { TranslateModule, TranslateService } from "@ngx-translate/core";
import { CommonModule } from "@angular/common";
import { MatProgressSpinnerModule } from "@angular/material/progress-spinner";
import { MatButtonModule } from "@angular/material/button";
import { GearService } from "../../services/gear.service";
import { ServiceComm } from "../../services/service-comm.service";
@@ -11,10 +14,17 @@ import { AddAction } from "../../models/add-action.enum";
import { NewGearComponent } from "../new-gear/new-gear.component";
@Component({
selector: "app-list-of-gears",
templateUrl: "./list-of-gears.component.html",
styleUrls: ["./list-of-gears.component.css"],
standalone: false
selector: "app-list-of-gears",
templateUrl: "./list-of-gears.component.html",
styleUrls: ["./list-of-gears.component.css"],
imports: [
TranslateModule,
CommonModule,
MatPaginatorModule,
MatProgressSpinnerModule,
MatTableModule,
MatButtonModule,
],
})
export class ListOfGearsComponent implements OnInit {
public displayedColumns: Array<string> = [
@@ -23,37 +33,38 @@ export class ListOfGearsComponent implements OnInit {
"maxSize",
"aad",
"mainCanopy",
"reserveCanopy"
"reserveCanopy",
];
public dataSourceTable: MatTableDataSource<GearResp>;
public resultsLength = 0;
@ViewChild(MatPaginator, { static: true }) paginator: MatPaginator;
constructor(private serviceApi: GearService,
private serviceComm: ServiceComm,
public dialog: MatDialog,
private translateService: TranslateService) {}
constructor(
private serviceApi: GearService,
private serviceComm: ServiceComm,
public dialog: MatDialog,
private translateService: TranslateService
) {}
ngOnInit() {
this.serviceComm.refreshRequest.subscribe(action => {
this.serviceComm.refreshRequest.subscribe((action) => {
if (action === AddAction.Gear) {
this.dialog.closeAll();
this.getListOfGears();
}
});
this.serviceComm.forceTranslateTitle.subscribe((data)=> {
if (data === true){
this.serviceComm.forceTranslateTitle.subscribe((data) => {
if (data === true) {
this.updateTitle();
}
});
this.updateTitle();
this.getListOfGears();
}
getListOfGears() {
this.serviceApi.getListOfGears()
.subscribe(data => {
this.serviceApi.getListOfGears().subscribe((data) => {
setTimeout(() => {
data.sort((a, b) => b.id - a.id);
this.dataSourceTable = new MatTableDataSource<GearResp>(data);
@@ -66,13 +77,13 @@ export class ListOfGearsComponent implements OnInit {
openDialogToAdd() {
this.dialog.open(NewGearComponent, {
height: "400px",
width: "600px"
width: "600px",
});
}
private updateTitle() {
this.translateService.get("ListGears_Title").subscribe(
data => { this.serviceComm.updatedComponentTitle(data); }
);
this.translateService.get("ListGears_Title").subscribe((data) => {
this.serviceComm.updatedComponentTitle(data);
});
}
}

View File

@@ -1,8 +1,20 @@
<div>
<form [formGroup]="imgForm" (ngSubmit)="onSubmit(imgForm.value)" autocomplete="off" style="padding: 10px;">
<form
[formGroup]="imgForm"
(ngSubmit)="onSubmit(imgForm.value)"
autocomplete="off"
style="padding: 10px"
>
<p>
<input type="file" #fileUpload id="fileUpload" name="fileUpload" accept="image/*" formControlName="image"
(change)="onFileChanged($event)" />
<input
type="file"
#fileUpload
id="fileUpload"
name="fileUpload"
accept="image/*"
formControlName="image"
(change)="onFileChanged($event)"
/>
</p>
<p>
<mat-form-field>
@@ -12,7 +24,7 @@
</p>
<button mat-icon-button color="warn" type="submit">
<mat-icon>file_upload</mat-icon>
<mat-icon svgIcon="file_upload"></mat-icon>
Upload image
</button>
<label>{{ imageError }}</label>
@@ -22,28 +34,54 @@
<div *ngIf="resultsLength > 0">
<table mat-table [dataSource]="dataSourceTable">
<ng-container matColumnDef="comment">
<th mat-header-cell *matHeaderCellDef style="text-align: center;">Comments</th>
<td mat-cell *matCellDef="let element" style="text-align: left;"><span style="white-space:nowrap;">{{element.comment}}</span></td>
<th mat-header-cell *matHeaderCellDef style="text-align: center">
Comments
</th>
<td mat-cell *matCellDef="let element" style="text-align: left">
<span style="white-space: nowrap">{{ element.comment }}</span>
</td>
</ng-container>
<ng-container matColumnDef="data">
<th mat-header-cell *matHeaderCellDef style="text-align: center;">Image</th>
<td mat-cell *matCellDef="let element" style="text-align: center;">
<img src="{{element.data}}" alt="image" style="width:50%;" (click)="openModal(element)" class="cursor">
<th mat-header-cell *matHeaderCellDef style="text-align: center">
Image
</th>
<td mat-cell *matCellDef="let element" style="text-align: center">
<img
src="{{ element.data }}"
alt="image"
style="width: 50%"
(click)="openModal(element)"
class="cursor"
/>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
</table>
<mat-paginator [length]="resultsLength" [pageSize]="10"></mat-paginator>
</div>
<div class="imgmodal" [ngStyle]="{'display': (showPopin === true) ? 'block' : 'none'}">
<div
class="imgmodal"
[ngStyle]="{ display: showPopin === true ? 'block' : 'none' }"
>
<span class="close cursor" (click)="closeModal()">&times;</span>
<mat-icon aria-hidden="false" aria-label="Rotation" (click)="rotate()" class="rotate cursor">undo</mat-icon>
<mat-icon
aria-hidden="false"
aria-label="Rotation"
(click)="rotate()"
class="rotate cursor"
svgIcon="rotate"
></mat-icon>
<div class="imgbox">
<img class="center-fit cursor" src="{{ popinImage }}" (click)="closeModal()" [@rotatedState]='stateRotation'>
<img
class="center-fit cursor"
src="{{ popinImage }}"
(click)="closeModal()"
[@rotatedState]="stateRotation"
/>
</div>
</div>

View File

@@ -1,30 +1,51 @@
import { Component, OnInit, ViewChild } from '@angular/core';
import { FormGroup, FormControl, Validators } from '@angular/forms';
import { MatTableDataSource } from '@angular/material/table';
import { MatPaginator } from '@angular/material/paginator';
import { trigger,state, style } from '@angular/animations';
import { Component, OnInit, ViewChild } from "@angular/core";
import {
FormGroup,
FormControl,
Validators,
ReactiveFormsModule,
} from "@angular/forms";
import { MatTableDataSource, MatTableModule } from "@angular/material/table";
import { MatPaginator, MatPaginatorModule } from "@angular/material/paginator";
import { trigger, state, style } from "@angular/animations";
import { CommonModule } from "@angular/common";
import { MatIconModule } from "@angular/material/icon";
import { MatFormFieldModule } from "@angular/material/form-field";
import { TranslateModule } from "@ngx-translate/core";
import { MatButtonModule } from "@angular/material/button";
import { MatInputModule } from "@angular/material/input";
import { ImageService } from '../../services/image.service';
import { ServiceComm } from '../../services/service-comm.service';
import { ImageResp } from '../../models/image';
import { AddAction } from '../../models/add-action.enum';
import { ImageService } from "../../services/image.service";
import { ServiceComm } from "../../services/service-comm.service";
import { ImageResp } from "../../models/image";
import { AddAction } from "../../models/add-action.enum";
@Component({
selector: 'app-list-of-images',
templateUrl: './list-of-images.component.html',
styleUrls: ['./list-of-images.component.css'],
animations: [
trigger('rotatedState', [
state('default', style({ transform: 'rotate(0)' })),
state('rot90', style({ transform: 'rotate(-90deg)' })),
state('rot180', style({ transform: 'rotate(-180deg)' })),
state('rot270', style({ transform: 'rotate(-270deg)' })),
])
],
standalone: false
selector: "app-list-of-images",
templateUrl: "./list-of-images.component.html",
styleUrls: ["./list-of-images.component.css"],
animations: [
trigger("rotatedState", [
state("default", style({ transform: "rotate(0)" })),
state("rot90", style({ transform: "rotate(-90deg)" })),
state("rot180", style({ transform: "rotate(-180deg)" })),
state("rot270", style({ transform: "rotate(-270deg)" })),
]),
],
imports: [
TranslateModule,
CommonModule,
MatIconModule,
MatPaginatorModule,
MatFormFieldModule,
ReactiveFormsModule,
MatTableModule,
MatButtonModule,
MatInputModule,
],
})
export class ListOfImagesComponent implements OnInit {
public displayedColumns: Array<string> = ['comment', 'data'];
public displayedColumns: Array<string> = ["comment", "data"];
public imgForm: FormGroup;
public imageError: string;
private selectedFile: string;
@@ -32,13 +53,13 @@ export class ListOfImagesComponent implements OnInit {
public showPopin: boolean;
public dataSourceTable: MatTableDataSource<ImageResp>;
public resultsLength = 0;
public stateRotation: string = 'default';
public stateRotation: string = "default";
@ViewChild(MatPaginator, { static: true }) paginator: MatPaginator;
constructor(
private serviceApi: ImageService,
private serviceComm: ServiceComm
) { }
private serviceComm: ServiceComm,
) {}
ngOnInit(): void {
this.serviceComm.refreshRequest.subscribe((action) => {
@@ -49,33 +70,32 @@ export class ListOfImagesComponent implements OnInit {
this.getListOfImages();
this.imgForm = new FormGroup({
comment: new FormControl('', Validators.required),
image: new FormControl('', Validators.required),
comment: new FormControl("", Validators.required),
image: new FormControl("", Validators.required),
});
}
private getListOfImages() {
this.serviceApi.getListOfImages()
.subscribe((data) => {
setTimeout(() => {
this.dataSourceTable = new MatTableDataSource<ImageResp>(data);
this.dataSourceTable.paginator = this.paginator;
this.resultsLength = data.length;
}, 500);
});
this.serviceApi.getListOfImages().subscribe((data) => {
setTimeout(() => {
this.dataSourceTable = new MatTableDataSource<ImageResp>(data);
this.dataSourceTable.paginator = this.paginator;
this.resultsLength = data.length;
}, 500);
});
}
public onFileChanged(fileInput: any) {
const file = fileInput.dataTransfer
? fileInput.dataTransfer.files[0]
: fileInput.target.files[0];
const allowed_types = ['image/png', 'image/jpeg'];
const allowed_types = ["image/png", "image/jpeg"];
const max_size = 20971520;
if (!allowed_types.includes(file.type)) {
this.imageError = 'Only Images are allowed ( JPG | PNG )';
this.imageError = "Only Images are allowed ( JPG | PNG )";
} else if (file.size > max_size) {
this.imageError = 'Maximum size allowed is ' + max_size / 1000 + 'Mb';
this.imageError = "Maximum size allowed is " + max_size / 1000 + "Mb";
} else {
const reader = new FileReader();
reader.onload = this.checkAndExtractDataToBase64.bind(this);
@@ -90,16 +110,16 @@ export class ListOfImagesComponent implements OnInit {
const image = new Image();
image.src = e.target.result;
image.onload = (rs) => {
const img_height = rs.currentTarget['height'];
const img_width = rs.currentTarget['width'];
const img_height = rs.currentTarget["height"];
const img_width = rs.currentTarget["width"];
if (img_height > max_height && img_width > max_width) {
this.imageError =
'Maximum dimentions allowed ' + max_height + '*' + max_width + 'px';
"Maximum dimentions allowed " + max_height + "*" + max_width + "px";
} else {
const imgBase64Path = e.target.result;
this.selectedFile = imgBase64Path;
this.imageError = 'OK';
this.imageError = "OK";
}
};
}
@@ -109,13 +129,14 @@ export class ListOfImagesComponent implements OnInit {
return;
}
this.serviceApi.addImage(formData.comment, this.selectedFile)
.subscribe(
() => { this.getListOfImages(); }
);
this.serviceApi
.addImage(formData.comment, this.selectedFile)
.subscribe(() => {
this.getListOfImages();
});
}
openModal(image: ImageResp){
openModal(image: ImageResp) {
this.popinImage = image.data;
this.showPopin = true;
}
@@ -125,14 +146,14 @@ export class ListOfImagesComponent implements OnInit {
}
rotate() {
if (this.stateRotation === 'default') {
this.stateRotation = 'rot90';
} else if (this.stateRotation === 'rot90') {
this.stateRotation = 'rot180';
} else if (this.stateRotation === 'rot180') {
this.stateRotation = 'rot270';
if (this.stateRotation === "default") {
this.stateRotation = "rot90";
} else if (this.stateRotation === "rot90") {
this.stateRotation = "rot180";
} else if (this.stateRotation === "rot180") {
this.stateRotation = "rot270";
} else {
this.stateRotation = 'default';
this.stateRotation = "default";
}
}
}

View File

@@ -1,20 +1,31 @@
<div class="content">
<div *ngIf="dataSourceTable != null else loading">
<button mat-raised-button color="accent" (click)="openDialogToAdd()" *ngIf="isUserAdmin == true">{{ 'List_JumpType_Add' | translate }}</button>
<div *ngIf="dataSourceTable != null; else loading">
<button
mat-raised-button
color="accent"
(click)="openDialogToAdd()"
*ngIf="isUserAdmin == true"
>
{{ "ListJumpType_Add" | translate }}
</button>
<table mat-table [dataSource]="dataSourceTable">
<ng-container matColumnDef="id">
<th mat-header-cell *matHeaderCellDef>{{ 'List_JumpType_Header_Id' | translate }}</th>
<td mat-cell *matCellDef="let element">{{element.id}}</td>
<th mat-header-cell *matHeaderCellDef>
{{ "ListJumpType_Header_Id" | translate }}
</th>
<td mat-cell *matCellDef="let element">{{ element.id }}</td>
</ng-container>
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef>{{ 'List_JumpType_Header_Name' | translate }}</th>
<td mat-cell *matCellDef="let element">{{element.name}}</td>
<th mat-header-cell *matHeaderCellDef>
{{ "ListJumpType_Header_Name" | translate }}
</th>
<td mat-cell *matCellDef="let element">{{ element.name }}</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
</table>
</div>
<mat-paginator [length]="resultsLength" [pageSize]="20"></mat-paginator>

View File

@@ -1,21 +1,31 @@
import { Component, OnInit, ViewChild } from "@angular/core";
import { MatPaginator } from "@angular/material/paginator";
import { MatTableDataSource } from "@angular/material/table";
import { MatPaginator, MatPaginatorModule } from "@angular/material/paginator";
import { MatTableDataSource, MatTableModule } from "@angular/material/table";
import { MatDialog } from "@angular/material/dialog";
import { TranslateService } from '@ngx-translate/core';
import { TranslateModule, TranslateService } from "@ngx-translate/core";
import { CommonModule } from "@angular/common";
import { MatProgressSpinnerModule } from "@angular/material/progress-spinner";
import { MatButtonModule } from "@angular/material/button";
import { AddAction } from "../../models/add-action.enum";
import { JumpTypeResp } from "../../models/jumpType";
import { JumpTypeService } from "../../services/jump-type.service";
import { ServiceComm } from "../../services/service-comm.service";
import { AuthenticationService } from '../../services/authentication.service';
import { AuthenticationService } from "../../services/authentication.service";
import { NewJumpTypeComponent } from "../new-jump-type/new-jump-type.component";
@Component({
selector: "app-list-of-jump-types",
templateUrl: "./list-of-jump-types.component.html",
styleUrls: ["./list-of-jump-types.component.css"],
standalone: false
selector: "app-list-of-jump-types",
templateUrl: "./list-of-jump-types.component.html",
styleUrls: ["./list-of-jump-types.component.css"],
imports: [
TranslateModule,
CommonModule,
MatPaginatorModule,
MatProgressSpinnerModule,
MatTableModule,
MatButtonModule,
],
})
export class ListOfJumpTypesComponent implements OnInit {
public displayedColumns: Array<string> = ["name"];
@@ -24,33 +34,36 @@ export class ListOfJumpTypesComponent implements OnInit {
public resultsLength = 0;
@ViewChild(MatPaginator, { static: true }) paginator: MatPaginator;
constructor(private serviceApi: JumpTypeService,
private serviceComm: ServiceComm,
private authenticationService: AuthenticationService,
public dialog: MatDialog,
private translateService: TranslateService) {
this.isUserAdmin = this.authenticationService.currentUserValue.roles === "admin";
constructor(
private serviceApi: JumpTypeService,
private serviceComm: ServiceComm,
private authenticationService: AuthenticationService,
public dialog: MatDialog,
private translateService: TranslateService
) {
this.isUserAdmin =
this.authenticationService.currentUserValue.roles === "admin";
}
ngOnInit() {
this.serviceComm.refreshRequest.subscribe(action => {
this.serviceComm.refreshRequest.subscribe((action) => {
if (action === AddAction.JumpType) {
this.dialog.closeAll();
this.getListOfJumpTypes();
}
});
this.serviceComm.forceTranslateTitle.subscribe((data)=> {
if (data === true){
this.serviceComm.forceTranslateTitle.subscribe((data) => {
if (data === true) {
this.updateTitle();
}
});
this.updateTitle();
this.getListOfJumpTypes();
}
getListOfJumpTypes() {
this.serviceApi.getListOfJumpTypes().subscribe(data => {
this.serviceApi.getListOfJumpTypes().subscribe((data) => {
setTimeout(() => {
data.sort((a, b) => a.name.localeCompare(b.name));
this.dataSourceTable = new MatTableDataSource<JumpTypeResp>(data);
@@ -65,8 +78,8 @@ export class ListOfJumpTypesComponent implements OnInit {
}
private updateTitle() {
this.translateService.get("ListJumpTypes_Title").subscribe(
data => { this.serviceComm.updatedComponentTitle(data); }
);
this.translateService.get("ListJumpTypes_Title").subscribe((data) => {
this.serviceComm.updatedComponentTitle(data);
});
}
}

View File

@@ -1,82 +1,165 @@
<div class="content">
<div>
<button mat-raised-button color="accent" [routerLink]="['/newjump']" [routerLinkActive]="['active']"
skipLocationChange>{{ 'List_Jump_Add' | translate }}</button>
</div>
<div>
<button
mat-raised-button
color="accent"
[routerLink]="['/newjump']"
[routerLinkActive]="['active']"
skipLocationChange
>
{{ "ListJump_Add" | translate }}
</button>
</div>
<mat-progress-bar [mode]="'indeterminate'" *ngIf="isLoading"></mat-progress-bar>
<mat-progress-bar
[mode]="'indeterminate'"
*ngIf="isLoading"
></mat-progress-bar>
<div>
<table mat-table [dataSource]="dataSourceTable">
<ng-container matColumnDef="infos">
<th mat-header-cell *matHeaderCellDef style="min-width: 80px;text-wrap: nowrap;"></th>
<td mat-cell *matCellDef="let element" style="text-align: left;text-wrap: nowrap;">
<mat-icon aria-hidden="false" aria-label="Additional informations of the jump"
style="cursor: pointer;" (click)='openDialog(element, false)'>info</mat-icon>
<mat-icon aria-hidden="false" aria-label="Special jump"
[style.visibility]="element.notes != undefined ? 'visible' : 'hidden'">sticky_note_2</mat-icon>
<mat-icon aria-hidden="false" aria-label="Special jump"
[style.visibility]="element.isSpecial ? 'visible' : 'hidden'">celebration</mat-icon>
</td>
</ng-container>
<div>
<table mat-table [dataSource]="dataSourceTable">
<ng-container matColumnDef="infos">
<th
mat-header-cell
*matHeaderCellDef
style="min-width: 80px; text-wrap: nowrap"
></th>
<td
mat-cell
*matCellDef="let element"
style="text-align: left; text-wrap: nowrap"
>
<mat-icon
aria-hidden="false"
aria-label="Additional informations of the jump"
style="cursor: pointer"
(click)="openDialog(element, false)"
svgIcon="info"
></mat-icon>
<mat-icon
aria-hidden="false"
aria-label="Notes"
[style.visibility]="
element.notes != undefined ? 'visible' : 'hidden'
"
svgIcon="note"
></mat-icon>
<mat-icon
aria-hidden="false"
aria-label="Special jump"
[style.visibility]="element.isSpecial ? 'visible' : 'hidden'"
svgIcon="celebration"
></mat-icon>
</td>
</ng-container>
<ng-container matColumnDef="id">
<th mat-header-cell *matHeaderCellDef style="min-width: 70px;">{{ 'List_Jump_Header_Num' | translate }}
</th>
<td mat-cell *matCellDef="let element; let i = index">
{{ paginator.length - ( (paginator.pageIndex * paginator.pageSize ) + i ) }}
</td>
</ng-container>
<ng-container matColumnDef="id">
<th mat-header-cell *matHeaderCellDef style="min-width: 70px">
{{ "ListJump_Header_Num" | translate }}
</th>
<td mat-cell *matCellDef="let element; let i = index">
{{
paginator.length - (paginator.pageIndex * paginator.pageSize + i)
}}
</td>
</ng-container>
<ng-container matColumnDef="jumpDate">
<th mat-header-cell *matHeaderCellDef>{{ 'List_Jump_Header_Date' | translate }}</th>
<td mat-cell *matCellDef="let element">
<span class="smallSpanWithBreakWord" [innerHTML]="element.jumpDate | date: 'yyyy-MM-dd'"></span>
</td>
</ng-container>
<ng-container matColumnDef="jumpDate">
<th mat-header-cell *matHeaderCellDef>
{{ "ListJump_Header_Date" | translate }}
</th>
<td mat-cell *matCellDef="let element">
<span
class="smallSpanWithBreakWord"
[innerHTML]="element.jumpDate | date: 'yyyy-MM-dd'"
></span>
</td>
</ng-container>
<ng-container matColumnDef="jumpType">
<th mat-header-cell *matHeaderCellDef style="min-width: 100px;text-wrap: nowrap;">{{ 'List_Jump_Header_JumpType' |
translate }}</th>
<td mat-cell *matCellDef="let element" style="text-wrap: nowrap;">
<span class="smallSpanWithBreakWord" [innerHTML]="element.jumpType.name"></span>
</td>
</ng-container>
<ng-container matColumnDef="jumpType">
<th
mat-header-cell
*matHeaderCellDef
style="min-width: 100px; text-wrap: nowrap"
>
{{ "ListJump_Header_JumpType" | translate }}
</th>
<td mat-cell *matCellDef="let element" style="text-wrap: nowrap">
<span
class="smallSpanWithBreakWord"
[innerHTML]="element.jumpType.name"
></span>
</td>
</ng-container>
<ng-container matColumnDef="aircraft">
<th mat-header-cell *matHeaderCellDef style="min-width: 110px;">{{ 'List_Jump_Header_Aircraft' |
translate }}</th>
<td mat-cell *matCellDef="let element">
<span class="smallSpanWithBreakWord" [innerHTML]="element.aircraft.name"></span>
</td>
</ng-container>
<ng-container matColumnDef="aircraft">
<th mat-header-cell *matHeaderCellDef style="min-width: 110px">
{{ "ListJump_Header_Aircraft" | translate }}
</th>
<td mat-cell *matCellDef="let element">
<span
class="smallSpanWithBreakWord"
[innerHTML]="element.aircraft.name"
></span>
</td>
</ng-container>
<ng-container matColumnDef="dropZone">
<th mat-header-cell *matHeaderCellDef>{{ 'List_Jump_Header_Dz' | translate }}</th>
<td mat-cell *matCellDef="let element">
<span class="spanWithBreakWord" [innerHTML]="element.dropZone.name"></span>
</td>
</ng-container>
<ng-container matColumnDef="dropZone">
<th mat-header-cell *matHeaderCellDef>
{{ "ListJump_Header_Dz" | translate }}
</th>
<td mat-cell *matCellDef="let element">
<span
class="spanWithBreakWord"
[innerHTML]="element.dropZone.name"
></span>
</td>
</ng-container>
<ng-container matColumnDef="gear">
<th mat-header-cell *matHeaderCellDef>{{ 'List_Jump_Header_Id' | translate }}</th>
<td mat-cell *matCellDef="let element">{{element.gear.name}}</td>
</ng-container>
<ng-container matColumnDef="gear">
<th mat-header-cell *matHeaderCellDef>
{{ "ListJump_Header_Id" | translate }}
</th>
<td mat-cell *matCellDef="let element">{{ element.gear.name }}</td>
</ng-container>
<ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef style="min-width: 80px;text-wrap: nowrap;"></th>
<td mat-cell *matCellDef="let element" style="text-align: left;text-wrap: nowrap;">
<mat-icon aria-hidden="false" aria-label="Delete this jump" style="cursor: pointer;"
(click)='delete(element)'>delete</mat-icon>
<mat-icon aria-hidden="false" aria-label="Update some informations of the jump"
style="cursor: pointer; margin-left: 10px;" (click)='openDialog(element, true)'>edit</mat-icon>
</td>
</ng-container>
<ng-container matColumnDef="actions">
<th
mat-header-cell
*matHeaderCellDef
style="min-width: 80px; text-wrap: nowrap"
></th>
<td
mat-cell
*matCellDef="let element"
style="text-align: left; text-wrap: nowrap"
>
<mat-icon
aria-hidden="false"
aria-label="Delete this jump"
style="cursor: pointer"
(click)="delete(element)"
svgIcon="delete"
></mat-icon>
<mat-icon
aria-hidden="false"
aria-label="Update some informations of the jump"
style="cursor: pointer; margin-left: 10px"
(click)="openDialog(element, true)"
svgIcon="edit"
></mat-icon>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
</div>
<tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
</table>
</div>
<mat-paginator #paginator [pageSize]="20" (page)="pageChanged($event)" showFirstLastButtons></mat-paginator>
</div>
<mat-paginator
#paginator
[pageSize]="20"
(page)="pageChanged($event)"
showFirstLastButtons
></mat-paginator>
</div>

View File

@@ -1,8 +1,21 @@
import { Component, OnInit, ViewChild } from "@angular/core";
import { MatPaginator, PageEvent } from "@angular/material/paginator";
import { MatTableDataSource } from "@angular/material/table";
import { RouterLink, RouterModule } from "@angular/router";
import {
MatPaginator,
MatPaginatorModule,
PageEvent,
} from "@angular/material/paginator";
import { MatTableDataSource, MatTableModule } from "@angular/material/table";
import { MatDialog } from "@angular/material/dialog";
import { TranslateService } from "@ngx-translate/core";
import { TranslateModule, TranslateService } from "@ngx-translate/core";
import { CommonModule } from "@angular/common";
import { MatIconModule } from "@angular/material/icon";
import { MatProgressSpinnerModule } from "@angular/material/progress-spinner";
import { MatProgressBarModule } from "@angular/material/progress-bar";
import { MatButtonModule } from "@angular/material/button";
import { MatInputModule } from "@angular/material/input";
import { ReactiveFormsModule } from "@angular/forms";
import { MatFormFieldModule } from "@angular/material/form-field";
import { AddAction } from "../../models/add-action.enum";
import { Jump } from "../../models/jump";
@@ -12,10 +25,24 @@ import { JumpInfosComponent } from "../jump-infos/jump-infos.component";
import { StatsService } from "../../services/stats.service";
@Component({
selector: "app-list-of-jumps",
templateUrl: "./list-of-jumps.component.html",
styleUrls: ["./list-of-jumps.component.css"],
standalone: false
selector: "app-list-of-jumps",
templateUrl: "./list-of-jumps.component.html",
styleUrls: ["./list-of-jumps.component.css"],
imports: [
TranslateModule,
CommonModule,
RouterLink,
RouterModule,
MatIconModule,
MatPaginatorModule,
MatProgressSpinnerModule,
MatProgressBarModule,
MatTableModule,
MatFormFieldModule,
ReactiveFormsModule,
MatInputModule,
MatButtonModule,
],
})
export class ListOfJumpsComponent implements OnInit {
public displayedColumns: Array<string> = [
@@ -31,11 +58,12 @@ export class ListOfJumpsComponent implements OnInit {
@ViewChild(MatPaginator, { static: true }) paginator: MatPaginator;
public isLoading: boolean = false;
constructor(private serviceApi: JumpService,
private serviceComm: ServiceComm,
public dialog: MatDialog,
private translateService: TranslateService,
private statsService: StatsService
constructor(
private serviceApi: JumpService,
private serviceComm: ServiceComm,
public dialog: MatDialog,
private translateService: TranslateService,
private statsService: StatsService
) {}
ngAferViewInit(): void {
@@ -62,15 +90,16 @@ export class ListOfJumpsComponent implements OnInit {
getListOfJumps(pageSize: number = 20, pageIndex: number = 0) {
this.isLoading = true;
this.serviceApi.getJumps(pageIndex * pageSize, pageIndex * pageSize + pageSize)
.subscribe((data) => {
this.dataSourceTable.data = data.rows;
setTimeout(() => {
this.paginator.length = data.count;
this.paginator.pageIndex = pageIndex;
this.isLoading = false;
}, 500);
});
this.serviceApi
.getJumps(pageIndex * pageSize, pageIndex * pageSize + pageSize)
.subscribe((data) => {
this.dataSourceTable.data = data.rows;
setTimeout(() => {
this.paginator.length = data.count;
this.paginator.pageIndex = pageIndex;
this.isLoading = false;
}, 500);
});
}
openDialog(item: Jump, editMode: boolean) {

View File

@@ -38,11 +38,11 @@
</mat-nav-list>
<div class="chart-container">
<!-- https://www.freecodecamp.org/news/how-to-make-bar-and-line-charts-using-chartjs-in-angular/ -->
<canvas
baseChart
[data]="barChartData"
[options]="barChartOptions"
[plugins]="barChartPlugins"
[legend]="barChartLegend"
[type]="barChartType"
>
@@ -111,7 +111,7 @@
<td mat-cell *matCellDef="let element">
<span
class="smallSpanWithBreakWord"
[innerHTML]="element.flightDate | date : 'yyyy-MM-dd'"
[innerHTML]="element.flightDate | date: 'yyyy-MM-dd'"
></span>
</td>
</ng-container>
@@ -124,8 +124,8 @@
aria-label="Delete this jump"
style="cursor: pointer"
(click)="delete(element)"
>delete</mat-icon
>
svgIcon="delete"
></mat-icon>
</td>
</ng-container>

View File

@@ -1,233 +1,278 @@
import { Component, OnInit } from '@angular/core';
import { formatDate } from '@angular/common';
import { TranslateService } from '@ngx-translate/core';
import { MatTableDataSource } from '@angular/material/table';
import { ChartConfiguration, ChartData, ChartType } from 'chart.js';
import { from, of, groupBy, mergeMap, reduce, map, Observable } from 'rxjs';
import { Component, OnInit } from "@angular/core";
import { RouterLink, RouterModule } from "@angular/router";
import { formatDate } from "@angular/common";
import { TranslateModule, TranslateService } from "@ngx-translate/core";
import { MatTableDataSource, MatTableModule } from "@angular/material/table";
import { CommonModule } from "@angular/common";
import { MatIconModule } from "@angular/material/icon";
import { MatListModule } from "@angular/material/list";
import { MatProgressSpinnerModule } from "@angular/material/progress-spinner";
import { MatRadioModule } from "@angular/material/radio";
import { MatProgressBarModule } from "@angular/material/progress-bar";
import { FormsModule } from "@angular/forms";
import { MatButtonModule } from "@angular/material/button";
import { ServiceComm } from '../../services/service-comm.service';
import { ChartConfiguration, ChartData, ChartType } from "chart.js";
import { from, groupBy, mergeMap, reduce, map } from "rxjs";
import { BaseChartDirective } from "ng2-charts";
import { ServiceComm } from "../../services/service-comm.service";
import { TunnelFlightService } from "../../services/tunnel-flight.service";
import { DateService } from '../../services/date.service';
import { TunnelFlight, TunnelFlightByMonth } from '../../models/tunnel-flight';
import { DateService } from "../../services/date.service";
import { TunnelFlight, TunnelFlightByMonth } from "../../models/tunnel-flight";
@Component({
selector: 'app-list-of-tunnel-flights',
templateUrl: './list-of-tunnel-flights.component.html',
styleUrls: ['./list-of-tunnel-flights.component.css'],
standalone: false
selector: "app-list-of-tunnel-flights",
templateUrl: "./list-of-tunnel-flights.component.html",
styleUrls: ["./list-of-tunnel-flights.component.css"],
imports: [
TranslateModule,
BaseChartDirective,
CommonModule,
MatIconModule,
MatListModule,
MatProgressSpinnerModule,
RouterLink,
MatRadioModule,
MatProgressBarModule,
FormsModule,
RouterModule,
MatTableModule,
MatButtonModule,
],
})
export class ListOfTunnelFlightsComponent implements OnInit {
public barChartLegend = true;
public barChartPlugins = [];
public barChartData: ChartData<'bar'>;
public barChartOptions: ChartConfiguration['options'];
public barChartType: ChartType;
public isLoading: boolean = false;
public selectedPeriod: string;
public dataSourceTable: MatTableDataSource<TunnelFlight> = new MatTableDataSource();
public displayedColumns: Array<string> = [
"id",
"tunnel",
"jumpType",
"nbMinutes",
"notes",
"flightDate",
"actions"
];
public stats: Array<{id: String|Number, values: String|Number}> = [];
public barChartLegend = true;
public barChartData: ChartData<"bar">;
public barChartOptions: ChartConfiguration["options"];
public barChartType: ChartType;
public isLoading: boolean = false;
public selectedPeriod: string;
public dataSourceTable: MatTableDataSource<TunnelFlight> =
new MatTableDataSource();
public displayedColumns: Array<string> = [
"id",
"tunnel",
"jumpType",
"nbMinutes",
"notes",
"flightDate",
"actions",
];
public stats: Array<{ id: String | Number; values: String | Number }> = [];
constructor(private serviceComm: ServiceComm,
private serviceTunnelFlight: TunnelFlightService,
private translateService: TranslateService,
private dateService: DateService) { }
constructor(
private serviceComm: ServiceComm,
private serviceTunnelFlight: TunnelFlightService,
private translateService: TranslateService,
private dateService: DateService
) {}
ngOnInit() {
this.serviceComm.forceTranslateTitle.subscribe((data) => {
if (data === true) {
this.updateTitle();
}
});
ngOnInit() {
this.serviceComm.forceTranslateTitle.subscribe((data) => {
if (data === true) {
this.updateTitle();
}
});
this.updateTitle();
this.chartConfig();
this.selectedPeriod = "currentYear"
this.getDataForGraph();
}
public onPeriodChange() {
this.getDataForGraph();
if (this.dataSourceTable?.data.length > 0){
this.getDataForTable();
}
this.chartConfig();
this.selectedPeriod = "currentYear";
this.getDataForGraph();
}
public onPeriodChange() {
this.getDataForGraph();
if (this.dataSourceTable?.data.length > 0) {
this.getDataForTable();
}
}
public onLoadTable() {
this.getDataForTable();
}
public onLoadTable() {
this.getDataForTable();
}
private chartConfig() {
this.barChartType = "bar";
this.barChartOptions = {
responsive: true,
maintainAspectRatio: true,
plugins: {
legend: {
display: true
},
tooltip: {
callbacks: {
footer: this.footer,
}
}
},
interaction: {
intersect: false,
mode: 'nearest',
axis: 'x'
},
scales: {
x: {
stacked: true
},
y: {
stacked: true
}
}
};
}
private updateTitle() {
this.translateService.get("ListTunnelFlight_Title").subscribe(
data => { this.serviceComm.updatedComponentTitle(data); }
);
}
private getDataForTable(): void {
this.isLoading = true;
// Get data to show in a table
let endDate = new Date();
endDate.setHours(0, 0, 0, 0);
let beginDate = this.computeBeginDateByPeriod(this.selectedPeriod, endDate);
this.serviceTunnelFlight.getTunnelFlights(beginDate, endDate)
.subscribe((data) => {
this.dataSourceTable.data = data;
this.isLoading = false;
});
}
private getDataForGraph(): void {
this.isLoading = true;
// Get data to show in a table
let endDate = new Date();
endDate.setHours(0, 0, 0, 0);
let beginDate = this.computeBeginDateByPeriod(this.selectedPeriod, endDate);
this.serviceTunnelFlight.getTunnelFlightsByMonth(beginDate, endDate)
.subscribe((data) => {
const allMonths = this.getMontsBetweenDates(beginDate, endDate)
const cumulatedTime = this.getCumulatedTimeByTypeByMonth(data, allMonths);
this.computeTimeByType(data);
this.barChartData = {
labels: allMonths,
datasets: cumulatedTime
};
this.isLoading = false;
});
}
private computeTimeByType(stats: TunnelFlightByMonth[]) {
this.stats = [];
from(stats).pipe(
groupBy((type) => type.type, { element: (p) => p.nb }),
mergeMap((group$) =>
group$.pipe(reduce((acc, cur) => [...acc, cur], [`${group$.key}`]))
),
map((arr) => ({ id: arr[0], values: arr.slice(1).reduce((a, b) => Number(a) + Number(b), 0) }))
)
.subscribe((p) => {
console.log(p);
this.stats.push(p);
});
}
private getMontsBetweenDates(beginDate: Date, endDate: Date): Array<string> {
let results: Array<string> = [];
let tmpBeginDate = new Date(beginDate.getTime());
const tmpEndDate = new Date(endDate.getTime());
while (tmpBeginDate < tmpEndDate) {
results.push(formatDate(tmpBeginDate, "yy-MM", "en"));
tmpBeginDate.setMonth(tmpBeginDate.getMonth() + 1);
}
return results;
}
private getCumulatedTimeByTypeByMonth(stats: TunnelFlightByMonth[], allMonths: string[]): Array<any> {
let tmpResults = new Map<string, number[]>();
const disctintType = Array.from(new Set(stats.map((item) => item.type)));
disctintType.forEach(type => {
tmpResults.set(type, Array.from({length: allMonths.length}, (v, k) => 0));
});
for (let i = 0; i < allMonths.length; i++) {
const month = allMonths[i];
let filteredStats = stats.filter((d) => d.month == month);
if (filteredStats.length > 0){
filteredStats.forEach(fs => {
tmpResults.get(fs.type)[i] += fs.nb;
});
}
}
const results = Array.from(tmpResults, function (item) {
return { label: item[0], data: item[1] }
});
return results;
}
private footer = (tooltipItems) => {
let sum = 0;
tooltipItems.forEach(function (tooltipItem) {
sum += tooltipItem.parsed.y;
});
return 'Sum: ' + sum;
private chartConfig() {
this.barChartType = "bar";
this.barChartOptions = {
responsive: true,
maintainAspectRatio: true,
plugins: {
legend: {
display: true,
},
tooltip: {
callbacks: {
footer: this.footer,
},
},
},
interaction: {
intersect: false,
mode: "nearest",
axis: "x",
},
scales: {
x: {
stacked: true,
},
y: {
stacked: true,
},
},
};
}
private computeBeginDateByPeriod(selectedPeriod: String, endDate: Date): Date {
let beginDate = new Date();
switch (selectedPeriod) {
case "currentYear":
beginDate = new Date(endDate.getFullYear(), 0, 1);
break;
case "12Months":
beginDate = this.dateService.addMonths(endDate, -12);
beginDate.setDate(1);
break;
case "all":
beginDate = this.dateService.addMonths(endDate, -120);
beginDate.setDate(1);
break;
}
private updateTitle() {
this.translateService.get("ListTunnelFlight_Title").subscribe((data) => {
this.serviceComm.updatedComponentTitle(data);
});
}
return beginDate;
}
private getDataForTable(): void {
this.isLoading = true;
public delete(item: TunnelFlight) {
let data: Array<TunnelFlight> = this.dataSourceTable.data;
data = data.filter((d) => d.id !== item.id);
// Get data to show in a table
let endDate = new Date();
endDate.setHours(0, 0, 0, 0);
let beginDate = this.computeBeginDateByPeriod(this.selectedPeriod, endDate);
this.serviceTunnelFlight
.getTunnelFlights(beginDate, endDate)
.subscribe((data) => {
this.dataSourceTable.data = data;
this.serviceTunnelFlight.deleteTunnelFlight(item);
this.isLoading = false;
});
}
private getDataForGraph(): void {
this.isLoading = true;
// Get data to show in a table
let endDate = new Date();
endDate.setHours(0, 0, 0, 0);
let beginDate = this.computeBeginDateByPeriod(this.selectedPeriod, endDate);
this.serviceTunnelFlight
.getTunnelFlightsByMonth(beginDate, endDate)
.subscribe((data) => {
const allMonths = this.getMontsBetweenDates(beginDate, endDate);
const cumulatedTime = this.getCumulatedTimeByTypeByMonth(
data,
allMonths
);
this.computeTimeByType(data);
this.barChartData = {
labels: allMonths,
datasets: cumulatedTime,
};
this.isLoading = false;
});
}
private computeTimeByType(stats: TunnelFlightByMonth[]) {
this.stats = [];
from(stats)
.pipe(
groupBy((type) => type.type, { element: (p) => p.nb }),
mergeMap((group$) =>
group$.pipe(reduce((acc, cur) => [...acc, cur], [`${group$.key}`]))
),
map((arr) => ({
id: arr[0],
values: arr.slice(1).reduce((a, b) => Number(a) + Number(b), 0),
}))
)
.subscribe((p) => {
console.log(p);
this.stats.push(p);
});
}
private getMontsBetweenDates(beginDate: Date, endDate: Date): Array<string> {
let results: Array<string> = [];
let tmpBeginDate = new Date(beginDate.getTime());
const tmpEndDate = new Date(endDate.getTime());
while (tmpBeginDate < tmpEndDate) {
results.push(formatDate(tmpBeginDate, "yy-MM", "en"));
tmpBeginDate.setMonth(tmpBeginDate.getMonth() + 1);
}
return results;
}
private getCumulatedTimeByTypeByMonth(
stats: TunnelFlightByMonth[],
allMonths: string[]
): Array<any> {
let tmpResults = new Map<string, number[]>();
const disctintType = Array.from(new Set(stats.map((item) => item.type)));
disctintType.forEach((type) => {
tmpResults.set(
type,
Array.from({ length: allMonths.length }, (v, k) => 0)
);
});
for (let i = 0; i < allMonths.length; i++) {
const month = allMonths[i];
let filteredStats = stats.filter((d) => d.month == month);
if (filteredStats.length > 0) {
filteredStats.forEach((fs) => {
tmpResults.get(fs.type)[i] += fs.nb;
});
}
}
const results = Array.from(tmpResults, function (item) {
return { label: item[0], data: item[1] };
});
return results;
}
private footer = (tooltipItems: any) => {
let sum = 0;
tooltipItems.forEach(function (tooltipItem: any) {
sum += tooltipItem.parsed.y;
});
return "Sum: " + sum;
};
private computeBeginDateByPeriod(
selectedPeriod: String,
endDate: Date
): Date {
let beginDate = new Date();
switch (selectedPeriod) {
case "currentYear":
beginDate = new Date(endDate.getFullYear(), 0, 1);
break;
case "12Months":
beginDate = this.dateService.addMonths(endDate, -12);
beginDate.setDate(1);
break;
case "all":
beginDate = this.dateService.addMonths(endDate, -120);
beginDate.setDate(1);
break;
}
return beginDate;
}
public delete(item: TunnelFlight) {
let data: Array<TunnelFlight> = this.dataSourceTable.data;
data = data.filter((d) => d.id !== item.id);
this.dataSourceTable.data = data;
this.serviceTunnelFlight.deleteTunnelFlight(item);
}
}

View File

@@ -1,36 +1,50 @@
<form focusInvalidInput autocomplete="off" style="padding: 10px;"
[formGroup]="loginForm" (ngSubmit)="onLoginSubmit()">
<form
focusInvalidInput
autocomplete="off"
style="padding: 10px"
[formGroup]="loginForm"
(ngSubmit)="onLoginSubmit()"
>
<p>
<mat-form-field>
<mat-label>{{ 'LoginUser_Username' | translate }}</mat-label>
<input type="text" matInput #username="matInput" formControlName="username"
[ngClass]="{ 'is-invalid': submitted && formCtrls.username.errors }">
<mat-error *ngIf="formCtrls.username.hasError('required')">
{{ 'LoginUser_UsernameRequired' | translate }}
<mat-label>{{ "LoginUser_Username" | translate }}</mat-label>
<input
type="text"
matInput
#username="matInput"
formControlName="username"
[ngClass]="{ 'is-invalid': submitted && formCtrls['username'].errors }"
/>
<mat-error *ngIf="formCtrls['username'].hasError('required')">
{{ "LoginUser_UsernameRequired" | translate }}
</mat-error>
<mat-error *ngIf="formCtrls.username.hasError('minlength')">
<mat-error *ngIf="formCtrls['username'].hasError('minlength')">
{{ 'LoginUser_UsernamePattern | translate }}
</mat-error>
</mat-form-field>
</p>
<p>
<mat-form-field>
<mat-label>{{ 'LoginUser_Password' | translate }}</mat-label>
<input type="password" matInput formControlName="password"
[ngClass]="{ 'is-invalid': submitted && formCtrls.password.errors }">
<mat-error *ngIf="formCtrls.password.hasError('required')">
{{ 'LoginUser_PasswordRequired' | translate }}
<mat-label>{{ "LoginUser_Password" | translate }}</mat-label>
<input
type="password"
matInput
formControlName="password"
[ngClass]="{ 'is-invalid': submitted && formCtrls['password'].errors }"
/>
<mat-error *ngIf="formCtrls['password'].hasError('required')">
{{ "LoginUser_PasswordRequired" | translate }}
</mat-error>
<mat-error *ngIf="formCtrls.password.hasError('pattern')">
{{ 'LoginUser_PasswordPattern' | translate }}
<mat-error *ngIf="formCtrls['password'].hasError('pattern')">
{{ "LoginUser_PasswordPattern" | translate }}
</mat-error>
</mat-form-field>
</p>
<button [disabled]="loading" mat-raised-button color="accent">
<span *ngIf="loading" class="spinner-border spinner-border-sm mr-1"></span>
{{ 'LoginUser_BtnLogin' | translate }}
{{ "LoginUser_BtnLogin" | translate }}
</button>
<div *ngIf="error" class="alert alert-danger mt-3 mb-0">{{error}}</div>
<div *ngIf="error" class="alert alert-danger mt-3 mb-0">{{ error }}</div>
</form>

View File

@@ -1,73 +1,88 @@
import { Component, OnInit, ViewChild, AfterViewInit } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { MatInput } from '@angular/material/input';
import { Component, OnInit, ViewChild, AfterViewInit } from "@angular/core";
import { Router, ActivatedRoute } from "@angular/router";
import { CommonModule } from "@angular/common";
import {
FormBuilder,
FormGroup,
ReactiveFormsModule,
Validators,
} from "@angular/forms";
import { TranslateModule, TranslatePipe } from "@ngx-translate/core";
import { MatInput, MatInputModule } from "@angular/material/input";
import { MatFormFieldModule } from "@angular/material/form-field";
import { MatButtonModule } from "@angular/material/button";
import { first } from 'rxjs/operators';
import { first } from "rxjs/operators";
import { AuthenticationService } from '../../services/authentication.service';
import { AuthenticationService } from "../../services/authentication.service";
@Component({
selector: 'app-login-user',
templateUrl: './login-user.component.html',
styleUrls: ['./login-user.component.css'],
standalone: false
selector: "app-login-user",
templateUrl: "./login-user.component.html",
styleUrls: ["./login-user.component.css"],
imports: [
CommonModule,
MatFormFieldModule,
ReactiveFormsModule,
MatInputModule,
TranslateModule,
TranslatePipe,
MatButtonModule,
],
})
export class LoginUserComponent implements OnInit, AfterViewInit {
loginForm: FormGroup;
loading = false;
submitted = false;
returnUrl: string;
error = '';
@ViewChild('username') userNameInput: MatInput;
export class LoginUserComponent implements OnInit {
public loginForm: FormGroup;
public loading = false;
public submitted = false;
public returnUrl: string;
public error: string = "";
constructor(private formBuilder: FormBuilder,
private route: ActivatedRoute,
private router: Router,
private authenticationService: AuthenticationService) {
constructor(
private formBuilder: FormBuilder,
private route: ActivatedRoute,
private router: Router,
private authenticationService: AuthenticationService
) {
if (this.authenticationService.currentUserValue) {
this.router.navigate(['/']);
this.router.navigate(["/"]);
}
}
ngAfterViewInit() {
this.userNameInput.focus();
}
ngOnInit() {
this.loginForm = this.formBuilder.group(
{
username: ['', [Validators.required, Validators.minLength(3)]],
password: ['', [Validators.required]]
username: ["", [Validators.required, Validators.minLength(3)]],
password: ["", [Validators.required]],
},
{ updateOn: 'submit' }
{ updateOn: "submit" }
);
// get return url from route parameters or default to '/'
this.returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/';
this.returnUrl = this.route.snapshot.queryParams["returnUrl"] || "/";
}
get formCtrls() {
return this.loginForm.controls;
}
onLoginSubmit() {
public onLoginSubmit() {
this.submitted = true;
if (this.loginForm.valid) {
this.loading = true;
this.authenticationService.login(this.formCtrls.username.value, this.formCtrls.password.value)
.pipe(first())
.subscribe(
data => {
this.router.navigate([this.returnUrl]);
},
error => {
this.error = error;
this.loading = false;
}
);
this.authenticationService
.login(
this.formCtrls["username"].value,
this.formCtrls["password"].value
)
.pipe(first())
.subscribe({
complete: () => this.router.navigate([this.returnUrl]),
error: (error) => {
this.error = error.message;
this.loading = false;
},
});
}
}
}

View File

@@ -1,18 +1,30 @@
<div class="content">
<mat-card style="max-width: 500px;" flex="50">
<mat-card-header style="align-items: center; justify-content: center; background-color: rgba(0,0,0,.03);">
<mat-card-title>{{ 'Login_Title' | translate }}</mat-card-title>
<mat-card style="max-width: 500px" flex="50">
<mat-card-header
style="
align-items: center;
justify-content: center;
background-color: rgba(0, 0, 0, 0.03);
"
>
<mat-card-title>{{ "Login_Title" | translate }}</mat-card-title>
<mat-select (selectionChange)="switchLang($event)" [(value)]="selectedLanguageFlag"
style="width: 60px; padding-left: 30px;" >
<mat-select
(selectionChange)="switchLang($event)"
[(value)]="selectedLanguageFlag"
style="width: 60px; padding-left: 30px"
>
<mat-select-trigger>
<img src="{{ 'assets/img/' + selectedLanguageFlag + '.svg' }}" style="width: 30px;">
<img
src="{{ 'assets/img/' + selectedLanguageFlag + '.svg' }}"
style="width: 30px"
/>
</mat-select-trigger>
<mat-option value="fr">
<img src="assets/img/fr.svg" style="width: 30px;">
<img src="assets/img/fr.svg" style="width: 30px" />
</mat-option>
<mat-option value="en">
<img src="assets/img/en.svg" style="width: 30px;">
<img src="assets/img/en.svg" style="width: 30px" />
</mat-option>
</mat-select>
</mat-card-header>

View File

@@ -1,18 +1,38 @@
import { Component, OnInit } from "@angular/core";
import { TranslateService } from "@ngx-translate/core";
import {
TranslateModule,
TranslatePipe,
TranslateService,
} from "@ngx-translate/core";
import { CommonModule } from "@angular/common";
import { MatSelectModule } from "@angular/material/select";
import { MatOptionModule } from "@angular/material/core";
import { MatCardModule } from "@angular/material/card";
import { MatTabsModule } from "@angular/material/tabs";
import { LoginUserComponent } from "../login-user/login-user.component";
import { CreateUserComponent } from "../create-user/create-user.component";
@Component({
selector: "app-login",
templateUrl: "./login.component.html",
styleUrls: ["./login.component.css"],
standalone: false
selector: "app-login",
templateUrl: "./login.component.html",
styleUrls: ["./login.component.css"],
imports: [
TranslateModule,
CommonModule,
MatSelectModule,
MatOptionModule,
MatCardModule,
MatTabsModule,
LoginUserComponent,
CreateUserComponent,
TranslateModule,
],
})
export class LoginComponent implements OnInit {
public selectedLanguageFlag: string;
constructor(private translate: TranslateService) {
translate.addLangs(['en', 'fr']);
translate.setDefaultLang('en');
this.selectedLanguageFlag = "en";
}

View File

@@ -1,52 +1,85 @@
import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import { AircraftService } from '../../services/aircraft.service';
import { ServiceComm } from '../../services/service-comm.service';
import { AddAction } from '../../models/add-action.enum';
import { Component, OnInit } from "@angular/core";
import {
FormControl,
FormGroup,
ReactiveFormsModule,
Validators,
} from "@angular/forms";
import { TranslateModule, TranslateService } from "@ngx-translate/core";
import { MatFormFieldModule } from "@angular/material/form-field";
import { MatInputModule } from "@angular/material/input";
import { MatButtonModule } from "@angular/material/button";
import { AddAction } from "../../models/add-action.enum";
import { AircraftService } from "../../services/aircraft.service";
import { ServiceComm } from "../../services/service-comm.service";
@Component({
selector: 'app-new-aircraft',
templateUrl: './new-aircraft.component.html',
styleUrls: ['./new-aircraft.component.css'],
standalone: false
selector: "app-new-aircraft",
templateUrl: "./new-aircraft.component.html",
styleUrls: ["./new-aircraft.component.css"],
imports: [
TranslateModule,
MatFormFieldModule,
ReactiveFormsModule,
MatFormFieldModule,
ReactiveFormsModule,
MatInputModule,
MatButtonModule,
],
})
export class NewAircraftComponent implements OnInit {
public addForm: FormGroup;
public imageError: string;
private selectedFile: string;
constructor(private serviceComm: ServiceComm,
private serviceApi: AircraftService) {
constructor(
private serviceComm: ServiceComm,
private serviceApi: AircraftService,
private translateService: TranslateService,
) {
this.addForm = new FormGroup(
{
aircraftName: new FormControl('', Validators.required)
aircraftName: new FormControl("", Validators.required),
},
{ updateOn: 'blur' }
{ updateOn: "blur" },
);
}
ngOnInit() { }
ngOnInit() {
this.serviceComm.forceTranslateTitle.subscribe((data) => {
if (data === true) {
this.updateTitle();
}
});
this.updateTitle();
}
onSubmit(formData) {
if (formData.invalid) {
return;
}
this.serviceApi.addAircraft(formData.aircraftName, this.selectedFile)
.subscribe(() => {
this.serviceComm.refreshData(AddAction.Aircraft);
});
this.serviceApi
.addAircraft(formData.aircraftName, this.selectedFile)
.subscribe(() => {
this.serviceComm.refreshData(AddAction.Aircraft);
});
}
public onFileChanged(fileInput: any) {
const file = fileInput.dataTransfer ? fileInput.dataTransfer.files[0] : fileInput.target.files[0];
const allowed_types = ['image/png', 'image/jpeg'];
const file = fileInput.dataTransfer
? fileInput.dataTransfer.files[0]
: fileInput.target.files[0];
const allowed_types = ["image/png", "image/jpeg"];
const max_size = 20971520;
if (!allowed_types.includes(file.type)) {
this.imageError = 'Only Images are allowed ( JPG | PNG )';
this.imageError = "Only Images are allowed ( JPG | PNG )";
} else if (file.size > max_size) {
this.imageError = 'Maximum size allowed is ' + max_size / 1000 + 'Mb';
this.imageError = "Maximum size allowed is " + max_size / 1000 + "Mb";
} else {
const reader = new FileReader();
reader.onload = this.checkAndExtractDataToBase64.bind(this);
@@ -61,17 +94,23 @@ export class NewAircraftComponent implements OnInit {
const image = new Image();
image.src = e.target.result;
image.onload = (rs) => {
const img_height = rs.currentTarget['height'];
const img_width = rs.currentTarget['width'];
const img_height = rs.currentTarget["height"];
const img_width = rs.currentTarget["width"];
if (img_height > max_height && img_width > max_width) {
this.imageError =
'Maximum dimentions allowed ' + max_height + '*' + max_width + 'px';
"Maximum dimentions allowed " + max_height + "*" + max_width + "px";
} else {
const imgBase64Path = e.target.result;
this.selectedFile = imgBase64Path;
this.imageError = 'OK';
this.imageError = "OK";
}
};
}
private updateTitle() {
this.translateService.get("NewAircraft_Title").subscribe((data) => {
this.serviceComm.updatedComponentTitle(data);
});
}
}

View File

@@ -1,43 +1,72 @@
import { Component, OnInit } from "@angular/core";
import { FormControl, FormGroup, Validators } from "@angular/forms";
import {
FormControl,
FormGroup,
ReactiveFormsModule,
Validators,
} from "@angular/forms";
import { TranslateModule, TranslateService } from "@ngx-translate/core";
import { MatFormFieldModule } from "@angular/material/form-field";
import { MatCheckboxModule } from "@angular/material/checkbox";
import { MatInputModule } from "@angular/material/input";
import { MatButtonModule } from "@angular/material/button";
import { AddAction } from "../../models/add-action.enum";
import { ServiceComm } from "../../services/service-comm.service";
import { DropzoneService } from "../../services/dropzone.service";
@Component({
selector: "app-new-drop-zone",
templateUrl: "./new-drop-zone.component.html",
styleUrls: ["./new-drop-zone.component.css"],
standalone: false
selector: "app-new-drop-zone",
templateUrl: "./new-drop-zone.component.html",
styleUrls: ["./new-drop-zone.component.css"],
imports: [
TranslateModule,
MatFormFieldModule,
MatCheckboxModule,
ReactiveFormsModule,
MatFormFieldModule,
MatInputModule,
MatButtonModule,
],
})
export class NewDropZoneComponent implements OnInit {
public addForm: FormGroup;
constructor(
private serviceComm: ServiceComm,
private dropzoneService: DropzoneService
private dropzoneService: DropzoneService,
private translateService: TranslateService,
) {
this.addForm = new FormGroup(
{
dzName: new FormControl("", Validators.required),
gps: new FormControl("x.x,y.y", [
Validators.required,
Validators.pattern("d+.d+,d+.d+")
Validators.pattern("d+.d+,d+.d+"),
]),
address: new FormControl("", Validators.required),
website: new FormControl("", Validators.required),
contactMail: new FormControl("", [
Validators.required,
Validators.email
Validators.email,
]),
isDz: new FormControl(true),
isTunnel: new FormControl(false)
isTunnel: new FormControl(false),
},
{ updateOn: "blur" }
{ updateOn: "blur" },
);
}
ngOnInit() {}
ngOnInit() {
this.serviceComm.forceTranslateTitle.subscribe((data) => {
if (data === true) {
this.updateTitle();
}
});
this.updateTitle();
}
onSubmit(formData) {
const splitGps: Array<string> = formData.gps.split(",");
@@ -50,16 +79,25 @@ export class NewDropZoneComponent implements OnInit {
dzType.push("tunnel");
}
this.dropzoneService.addDropZone(splitGps[0],
splitGps[1],
formData.dzName,
formData.address,
formData.website,
formData.contactMail,
dzType,
false)
.subscribe(() => {
this.serviceComm.refreshData(AddAction.Dropzone);
});
this.dropzoneService
.addDropZone(
splitGps[0],
splitGps[1],
formData.dzName,
formData.address,
formData.website,
formData.contactMail,
dzType,
false,
)
.subscribe(() => {
this.serviceComm.refreshData(AddAction.Dropzone);
});
}
private updateTitle() {
this.translateService.get("NewDz_Title").subscribe((data) => {
this.serviceComm.updatedComponentTitle(data);
});
}
}

View File

@@ -1,22 +1,40 @@
import { Component, OnInit } from "@angular/core";
import { FormControl, FormGroup, Validators } from "@angular/forms";
import {
FormControl,
FormGroup,
ReactiveFormsModule,
Validators,
} from "@angular/forms";
import { MatFormFieldModule } from "@angular/material/form-field";
import { TranslateModule, TranslateService } from "@ngx-translate/core";
import { MatInputModule } from "@angular/material/input";
import { MatButtonModule } from "@angular/material/button";
import { ServiceComm } from "../../services/service-comm.service";
import { GearService } from "../../services/gear.service";
import { AddAction } from "../../models/add-action.enum";
@Component({
selector: "app-new-gear",
templateUrl: "./new-gear.component.html",
styleUrls: ["./new-gear.component.css"],
standalone: false
selector: "app-new-gear",
templateUrl: "./new-gear.component.html",
styleUrls: ["./new-gear.component.css"],
imports: [
TranslateModule,
MatFormFieldModule,
ReactiveFormsModule,
ReactiveFormsModule,
MatInputModule,
MatButtonModule,
],
})
export class NewGearComponent implements OnInit {
public addForm: FormGroup;
constructor(private serviceComm: ServiceComm,
private serviceApi: GearService)
{
constructor(
private serviceComm: ServiceComm,
private serviceApi: GearService,
private translateService: TranslateService,
) {
this.addForm = new FormGroup(
{
name: new FormControl("", Validators.required),
@@ -24,41 +42,58 @@ export class NewGearComponent implements OnInit {
minSize: new FormControl("", [
Validators.required,
Validators.min(60),
Validators.max(320)
Validators.max(320),
]),
maxSize: new FormControl("", [
Validators.required,
Validators.min(60),
Validators.max(320)
Validators.max(320),
]),
aad: new FormControl("", Validators.required),
mainCanopy: new FormControl("", [
Validators.required,
Validators.min(60),
Validators.max(320)
Validators.max(320),
]),
reserveCanopy: new FormControl("", [
Validators.required,
Validators.min(60),
Validators.max(320)
])
Validators.max(320),
]),
},
{ updateOn: "blur" }
{ updateOn: "blur" },
);
}
ngOnInit() { }
ngOnInit() {
this.serviceComm.forceTranslateTitle.subscribe((data) => {
if (data === true) {
this.updateTitle();
}
});
this.updateTitle();
}
onSubmit(formData) {
this.serviceApi.addGear(formData.name,
formData.manufacturer,
+formData.minSize,
+formData.maxSize,
formData.aad,
formData.mainCanopy,
formData.reserveCanopy)
.subscribe(() => {
this.serviceComm.refreshData(AddAction.Gear);
});
this.serviceApi
.addGear(
formData.name,
formData.manufacturer,
+formData.minSize,
+formData.maxSize,
formData.aad,
formData.mainCanopy,
formData.reserveCanopy,
)
.subscribe(() => {
this.serviceComm.refreshData(AddAction.Gear);
});
}
private updateTitle() {
this.translateService.get("NewGear_Title").subscribe((data) => {
this.serviceComm.updatedComponentTitle(data);
});
}
}

View File

@@ -1,34 +1,66 @@
import { Component, OnInit } from "@angular/core";
import { FormControl, FormGroup, Validators } from "@angular/forms";
import {
FormControl,
FormGroup,
ReactiveFormsModule,
Validators,
} from "@angular/forms";
import { TranslateModule, TranslateService } from "@ngx-translate/core";
import { MatFormFieldModule } from "@angular/material/form-field";
import { MatInputModule } from "@angular/material/input";
import { MatButtonModule } from "@angular/material/button";
import { AddAction } from "../../models/add-action.enum";
import { ServiceComm } from "../../services/service-comm.service";
import { JumpTypeService } from "../../services/jump-type.service";
@Component({
selector: "app-new-jump-type",
templateUrl: "./new-jump-type.component.html",
styleUrls: ["./new-jump-type.component.css"],
standalone: false
selector: "app-new-jump-type",
templateUrl: "./new-jump-type.component.html",
styleUrls: ["./new-jump-type.component.css"],
imports: [
TranslateModule,
MatFormFieldModule,
ReactiveFormsModule,
MatInputModule,
MatButtonModule,
],
})
export class NewJumpTypeComponent implements OnInit {
public addForm: FormGroup;
constructor(private serviceComm: ServiceComm,
private jumpTypeService: JumpTypeService) {
constructor(
private serviceComm: ServiceComm,
private jumpTypeService: JumpTypeService,
private translateService: TranslateService,
) {
this.addForm = new FormGroup(
{
jumptypeName: new FormControl("", Validators.required)
jumptypeName: new FormControl("", Validators.required),
},
{ updateOn: "blur" }
{ updateOn: "blur" },
);
}
ngOnInit() { }
ngOnInit() {
this.serviceComm.forceTranslateTitle.subscribe((data) => {
if (data === true) {
this.updateTitle();
}
});
this.updateTitle();
}
onSubmit(formData) {
this.jumpTypeService.addJumpType(formData.jumptypeName)
.subscribe(() => {
this.serviceComm.refreshData(AddAction.JumpType);
});
this.jumpTypeService.addJumpType(formData.jumptypeName).subscribe(() => {
this.serviceComm.refreshData(AddAction.JumpType);
});
}
private updateTitle() {
this.translateService.get("NewJumpType_Title").subscribe((data) => {
this.serviceComm.updatedComponentTitle(data);
});
}
}

View File

@@ -1,119 +1,267 @@
<div class="content">
<div>
<button mat-raised-button color="accent" [routerLink]="['/jumps']" [routerLinkActive]="['active']" skipLocationChange>{{ 'NewJump_GoToJump' | translate }}</button>
<p><mat-checkbox [(ngModel)]="resetForm" labelPosition="before">{{ 'NewJump_ResetForm' | translate }}</mat-checkbox></p>
<button
mat-raised-button
color="accent"
[routerLink]="['/jumps']"
[routerLinkActive]="['active']"
skipLocationChange
>
{{ "NewJump_GoToJump" | translate }}
</button>
<p>
<mat-checkbox [(ngModel)]="resetForm" labelPosition="before">{{
"NewJump_ResetForm" | translate
}}</mat-checkbox>
</p>
</div>
<form class="formNewJumps" (ngSubmit)="onFormSubmit()" *ngIf="notLoadingToDisplay() else loading">
<form
class="formNewJumps"
(ngSubmit)="onFormSubmit()"
*ngIf="notLoadingToDisplay(); else loading"
>
<mat-form-field>
<mat-label>{{ 'NewJump_ChooseJumpType' | translate }}</mat-label>
<input type="text" matInput [matAutocomplete]="autoJumpType" [(ngModel)]="selectedJumpType"
name="selectedJumpType">
<mat-autocomplete #autoJumpType="matAutocomplete" [displayWith]="displayNameFn">
<mat-label>{{ "NewJump_ChooseJumpType" | translate }}</mat-label>
<input
type="text"
matInput
[matAutocomplete]="autoJumpType"
[(ngModel)]="selectedJumpType"
name="selectedJumpType"
/>
<mat-autocomplete
#autoJumpType="matAutocomplete"
[displayWith]="displayNameFn"
>
<mat-option *ngFor="let jumpType of listOfJumpType" [value]="jumpType">
{{jumpType.name}}
{{ jumpType.name }}
</mat-option>
</mat-autocomplete>
<button *ngIf="selectedJumpType" matSuffix mat-icon-button aria-label="Clear"
(click)="selectedJumpType=undefined">
<button
*ngIf="selectedJumpType"
matSuffix
mat-icon-button
aria-label="Clear"
(click)="selectedJumpType = undefined"
>
<mat-icon>close</mat-icon>
</button>
</mat-form-field>
<mat-form-field>
<mat-label>{{ 'NewJump_ChooseAircraft' | translate }}</mat-label>
<input type="text" matInput [matAutocomplete]="autoAircraft" [(ngModel)]="selectedAircraft"
name="selectedAircraft">
<mat-autocomplete #autoAircraft="matAutocomplete" [displayWith]="displayNameFn">
<mat-label>{{ "NewJump_ChooseAircraft" | translate }}</mat-label>
<input
type="text"
matInput
[matAutocomplete]="autoAircraft"
[(ngModel)]="selectedAircraft"
name="selectedAircraft"
/>
<mat-autocomplete
#autoAircraft="matAutocomplete"
[displayWith]="displayNameFn"
>
<mat-option *ngFor="let aircraft of listOfAircraft" [value]="aircraft">
{{aircraft.name}}
{{ aircraft.name }}
</mat-option>
</mat-autocomplete>
<button *ngIf="selectedAircraft" matSuffix mat-icon-button aria-label="Clear"
(click)="selectedAircraft=undefined">
<button
*ngIf="selectedAircraft"
matSuffix
mat-icon-button
aria-label="Clear"
(click)="selectedAircraft = undefined"
>
<mat-icon>close</mat-icon>
</button>
</mat-form-field>
<mat-form-field>
<mat-label>{{ 'NewJump_ChooseDz' | translate }}</mat-label>
<input type="text" matInput [matAutocomplete]="autoDropZone" [(ngModel)]="selectedDz"
(ngModelChange)="onChangeDz($event)" name="selectedDz">
<mat-autocomplete #autoDropZone="matAutocomplete" [displayWith]="displayNameFn">
<mat-option *ngFor="let dropZone of listOfFilteredDropZone" [value]="dropZone">
{{dropZone.name}}
<mat-icon aria-hidden="false" aria-label="Favorite" *ngIf="dropZone.isFavorite === true" color="primary">
favorite</mat-icon>
<mat-label>{{ "NewJump_ChooseDz" | translate }}</mat-label>
<input
type="text"
matInput
[matAutocomplete]="autoDropZone"
[(ngModel)]="selectedDz"
(ngModelChange)="onChangeDz($event)"
name="selectedDz"
/>
<mat-autocomplete
#autoDropZone="matAutocomplete"
[displayWith]="displayNameFn"
>
<mat-option
*ngFor="let dropZone of listOfFilteredDropZone"
[value]="dropZone"
>
{{ dropZone.name }}
<mat-icon
aria-hidden="false"
aria-label="Favorite"
*ngIf="dropZone.isFavorite === true"
color="primary"
>
favorite</mat-icon
>
</mat-option>
</mat-autocomplete>
<button *ngIf="selectedDz" matSuffix mat-icon-button aria-label="Clear" (click)="resetDz()">
<button
*ngIf="selectedDz"
matSuffix
mat-icon-button
aria-label="Clear"
(click)="resetDz()"
>
<mat-icon>close</mat-icon>
</button>
</mat-form-field>
<mat-form-field>
<mat-label>{{ 'NewJump_ChooseGear' | translate }}</mat-label>
<input type="text" matInput [matAutocomplete]="autoGear" [(ngModel)]="selectedGear" name="selectedGear">
<mat-autocomplete #autoGear="matAutocomplete" [displayWith]="displayGearFn">
<mat-label>{{ "NewJump_ChooseGear" | translate }}</mat-label>
<input
type="text"
matInput
[matAutocomplete]="autoGear"
[(ngModel)]="selectedGear"
name="selectedGear"
/>
<mat-autocomplete
#autoGear="matAutocomplete"
[displayWith]="displayGearFn"
>
<mat-option *ngFor="let gear of listOfGear" [value]="gear">
{{gear.name}} ({{gear.mainCanopy}})
{{ gear.name }} ({{ gear.mainCanopy }})
</mat-option>
</mat-autocomplete>
<button *ngIf="selectedGear" matSuffix mat-icon-button aria-label="Clear"
(click)="selectedGear=undefined">
<button
*ngIf="selectedGear"
matSuffix
mat-icon-button
aria-label="Clear"
(click)="selectedGear = undefined"
>
<mat-icon>close</mat-icon>
</button>
</mat-form-field>
<mat-checkbox [(ngModel)]="withCutaway" name="withCutaway">{{ 'NewJump_Cutaway' | translate }}</mat-checkbox>
<mat-checkbox [(ngModel)]="isSpecial" name="isSpecial">{{ 'NewJump_Special' | translate }}</mat-checkbox>
<mat-checkbox [(ngModel)]="withCutaway" name="withCutaway">{{
"NewJump_Cutaway" | translate
}}</mat-checkbox>
<mat-checkbox [(ngModel)]="isSpecial" name="isSpecial">{{
"NewJump_Special" | translate
}}</mat-checkbox>
<mat-form-field>
<input matInput [matDatepicker]="beginDateDp" [(ngModel)]="beginDate"
name="beginDate" disabled (ngModelChange)="onChangeBeginDate($event)">
<mat-datepicker-toggle matSuffix [for]="beginDateDp"></mat-datepicker-toggle>
<input
matInput
[matDatepicker]="beginDateDp"
[(ngModel)]="beginDate"
name="beginDate"
disabled
(ngModelChange)="onChangeBeginDate($event)"
/>
<mat-datepicker-toggle
matSuffix
[for]="beginDateDp"
></mat-datepicker-toggle>
<mat-datepicker #beginDateDp disabled="false"></mat-datepicker>
</mat-form-field>
<mat-form-field>
<input matInput [matDatepicker]="endDateDp" [(ngModel)]="endDate" name="endDate" disabled>
<mat-datepicker-toggle matSuffix [for]="endDateDp"></mat-datepicker-toggle>
<input
matInput
[matDatepicker]="endDateDp"
[(ngModel)]="endDate"
name="endDate"
disabled
/>
<mat-datepicker-toggle
matSuffix
[for]="endDateDp"
></mat-datepicker-toggle>
<mat-datepicker #endDateDp disabled="false"></mat-datepicker>
</mat-form-field>
<mat-form-field>
<input matInput placeholder="{{ 'NewJump_ExitAlt' | translate }}" [(ngModel)]="exitAltitude" name="exitAltitude" type="number">
<button *ngIf="exitAltitude" matSuffix mat-icon-button aria-label="Clear"
(click)="exitAltitude=undefined">
<input
matInput
placeholder="{{ 'NewJump_ExitAlt' | translate }}"
[(ngModel)]="exitAltitude"
name="exitAltitude"
type="number"
/>
<button
*ngIf="exitAltitude"
matSuffix
mat-icon-button
aria-label="Clear"
(click)="exitAltitude = undefined"
>
<mat-icon>close</mat-icon>
</button>
</mat-form-field>
<mat-form-field>
<input matInput placeholder="{{ 'NewJump_DeployAlt' | translate }}" [(ngModel)]="deployAltitude" name="deployAltitude" type="number">
<button *ngIf="deployAltitude" matSuffix mat-icon-button aria-label="Clear"
(click)="deployAltitude=undefined">
<input
matInput
placeholder="{{ 'NewJump_DeployAlt' | translate }}"
[(ngModel)]="deployAltitude"
name="deployAltitude"
type="number"
/>
<button
*ngIf="deployAltitude"
matSuffix
mat-icon-button
aria-label="Clear"
(click)="deployAltitude = undefined"
>
<mat-icon>close</mat-icon>
</button>
</mat-form-field>
<mat-form-field>
<input matInput placeholder="{{ 'NewJump_Count' | translate }}" [(ngModel)]="countOfJumps" name="countOfJumps" type="number">
<button *ngIf="countOfJumps" matSuffix mat-icon-button aria-label="Clear"
(click)="countOfJumps=undefined">
<input
matInput
placeholder="{{ 'NewJump_Count' | translate }}"
[(ngModel)]="countOfJumps"
name="countOfJumps"
type="number"
/>
<button
*ngIf="countOfJumps"
matSuffix
mat-icon-button
aria-label="Clear"
(click)="countOfJumps = undefined"
>
<mat-icon>close</mat-icon>
</button>
</mat-form-field>
<mat-form-field>
<textarea matInput placeholder="{{ 'NewJump_Comments' | translate }}" [(ngModel)]="comments" name="comments" type="text"></textarea>
<button *ngIf="comments" matSuffix mat-icon-button aria-label="Clear"
(click)="comments=undefined">
<textarea
matInput
placeholder="{{ 'NewJump_Comments' | translate }}"
[(ngModel)]="comments"
name="comments"
type="text"
></textarea>
<button
*ngIf="comments"
matSuffix
mat-icon-button
aria-label="Clear"
(click)="comments = undefined"
>
<mat-icon>close</mat-icon>
</button>
</mat-form-field>
<br />
<button mat-raised-button color="accent" *ngIf="isValidatedForm()">{{ 'NewJump_Submit' | translate }}</button>
<button mat-raised-button color="accent" *ngIf="isValidatedForm()">
{{ "NewJump_Submit" | translate }}
</button>
</form>
<ng-template #loading>

View File

@@ -1,7 +1,23 @@
import { Component, OnInit } from "@angular/core";
import { formatDate } from '@angular/common';
import { DateAdapter, MAT_DATE_FORMATS, NativeDateAdapter } from "@angular/material/core";
import { TranslateService } from '@ngx-translate/core';
import { RouterLink, RouterModule } from "@angular/router";
import { formatDate } from "@angular/common";
import {
DateAdapter,
MAT_DATE_FORMATS,
NativeDateAdapter,
} from "@angular/material/core";
import { TranslateModule, TranslateService } from "@ngx-translate/core";
import { CommonModule } from "@angular/common";
import { MatIconModule } from "@angular/material/icon";
import { MatOptionModule } from "@angular/material/core";
import { MatFormFieldModule } from "@angular/material/form-field";
import { MatCheckboxModule } from "@angular/material/checkbox";
import { MatAutocompleteModule } from "@angular/material/autocomplete";
import { MatDatepickerModule } from "@angular/material/datepicker";
import { MatProgressSpinnerModule } from "@angular/material/progress-spinner";
import { FormsModule, ReactiveFormsModule } from "@angular/forms";
import { MatInputModule } from "@angular/material/input";
import { MatButtonModule } from "@angular/material/button";
import { JumpTypeResp } from "../../models/jumpType";
import { AircraftResp } from "../../models/aircraft";
@@ -17,31 +33,47 @@ import { JumpTypeService } from "../../services/jump-type.service";
import { GearService } from "../../services/gear.service";
import { StatsService } from "../../services/stats.service";
export const PICK_FORMATS = {
parse: { dateInput: 'yy MM dd' },
parse: { dateInput: "yy MM dd" },
display: {
dateInput: 'yyyy-MM-dd',
monthYearLabel: 'yyyy MMM',
dateA11yLabel: 'yyyy MM dd',
monthYearA11yLabel: 'yyyy MMMM',
}
dateInput: "yyyy-MM-dd",
monthYearLabel: "yyyy MMM",
dateA11yLabel: "yyyy MM dd",
monthYearA11yLabel: "yyyy MMMM",
},
};
class PickDateAdapter extends NativeDateAdapter {
format(date: Date, displayFormat: Object): string {
return formatDate(date, displayFormat.toString(), "en");
override format(date: Date, displayFormat: Object): string {
return formatDate(date, displayFormat.toString(), "en");
}
}
@Component({
selector: "app-new-jump",
templateUrl: "./new-jump.component.html",
styleUrls: ["./new-jump.component.css"],
providers: [
{ provide: DateAdapter, useClass: PickDateAdapter },
{ provide: MAT_DATE_FORMATS, useValue: PICK_FORMATS }
],
standalone: false
selector: "app-new-jump",
templateUrl: "./new-jump.component.html",
styleUrls: ["./new-jump.component.css"],
providers: [
{ provide: DateAdapter, useClass: PickDateAdapter },
{ provide: MAT_DATE_FORMATS, useValue: PICK_FORMATS },
],
imports: [
TranslateModule,
CommonModule,
RouterLink,
FormsModule,
RouterModule,
MatIconModule,
MatOptionModule,
MatFormFieldModule,
MatCheckboxModule,
MatAutocompleteModule,
MatDatepickerModule,
MatProgressSpinnerModule,
MatFormFieldModule,
ReactiveFormsModule,
MatInputModule,
MatButtonModule,
],
})
export class NewJumpComponent implements OnInit {
public beginDate: Date;
@@ -66,26 +98,28 @@ export class NewJumpComponent implements OnInit {
private listOfDropZone: Array<DropZoneResp>;
public maxDate: Date;
constructor(private serviceComm: ServiceComm,
private serviceJump: JumpService,
private serviceJumpType: JumpTypeService,
private serviceAircraft: AircraftService,
private serviceDropzone: DropzoneService,
private serviceGear: GearService,
private dateService: DateService,
private translateService: TranslateService,
private statsService : StatsService) {}
constructor(
private serviceComm: ServiceComm,
private serviceJump: JumpService,
private serviceJumpType: JumpTypeService,
private serviceAircraft: AircraftService,
private serviceDropzone: DropzoneService,
private serviceGear: GearService,
private dateService: DateService,
private translateService: TranslateService,
private statsService: StatsService
) {}
ngOnInit() {
this.serviceComm.forceTranslateTitle.subscribe((data)=> {
if (data === true){
this.serviceComm.forceTranslateTitle.subscribe((data) => {
if (data === true) {
this.updateTitle();
}
});
this.updateTitle();
this.maxDate = this.dateService.addDays(new Date(), 1);
this.pendingAddRequest = false;
this.initForm();
this.getListOfJumpTypes();
@@ -94,87 +128,92 @@ export class NewJumpComponent implements OnInit {
onFormSubmit() {
this.pendingAddRequest = true;
this.serviceJump.addListOfJump(this.selectedJumpType.id,
this.selectedAircraft.id,
this.selectedDz.id,
this.selectedGear.id,
this.withCutaway === undefined ? false : this.withCutaway,
this.beginDate,
this.endDate,
this.exitAltitude,
this.deployAltitude,
this.countOfJumps,
this.comments,
this.isSpecial === undefined ? false : this.isSpecial)
.subscribe(() => {
this.statsService.resetStats();
this.comments = undefined;
this.withCutaway = false;
this.isSpecial = false;
this.serviceJump
.addListOfJump(
this.selectedJumpType.id,
this.selectedAircraft.id,
this.selectedDz.id,
this.selectedGear.id,
this.withCutaway === undefined ? false : this.withCutaway,
this.beginDate,
this.endDate,
this.exitAltitude,
this.deployAltitude,
this.countOfJumps,
this.comments,
this.isSpecial === undefined ? false : this.isSpecial
)
.subscribe(() => {
this.statsService.resetStats();
this.comments = undefined;
this.withCutaway = false;
this.isSpecial = false;
if (this.resetForm === true) {
this.initForm();
}
this.pendingAddRequest = false;
});
if (this.resetForm === true) {
this.initForm();
}
this.pendingAddRequest = false;
});
}
public isValidatedForm(): boolean {
return (this.selectedDz !== undefined &&
this.selectedDz.id !== undefined &&
this.selectedGear !== undefined &&
this.selectedGear.id !== undefined &&
this.selectedAircraft !== undefined &&
this.selectedAircraft.id !== undefined &&
this.selectedJumpType !== undefined &&
this.selectedJumpType.id !== undefined &&
this.exitAltitude !== undefined &&
typeof this.exitAltitude === "number" &&
this.deployAltitude !== undefined &&
typeof this.deployAltitude === "number" &&
this.countOfJumps !== undefined &&
typeof this.countOfJumps === "number");
return (
this.selectedDz !== undefined &&
this.selectedDz.id !== undefined &&
this.selectedGear !== undefined &&
this.selectedGear.id !== undefined &&
this.selectedAircraft !== undefined &&
this.selectedAircraft.id !== undefined &&
this.selectedJumpType !== undefined &&
this.selectedJumpType.id !== undefined &&
this.exitAltitude !== undefined &&
typeof this.exitAltitude === "number" &&
this.deployAltitude !== undefined &&
typeof this.deployAltitude === "number" &&
this.countOfJumps !== undefined &&
typeof this.countOfJumps === "number"
);
}
private getListOfJumpTypes() {
this.serviceJumpType.getListOfJumpTypes()
.subscribe((data) => {
data.sort((a, b) => a.name.localeCompare(b.name));
this.listOfJumpType = data;
this.countDatasLoaded = 1;
this.serviceJumpType.getListOfJumpTypes().subscribe((data) => {
data.sort((a, b) => a.name.localeCompare(b.name));
this.listOfJumpType = data;
this.countDatasLoaded = 1;
this.getListOfAircrafts();
this.getListOfDropZones();
this.getListOfGears();
});
this.getListOfAircrafts();
this.getListOfDropZones();
this.getListOfGears();
});
}
private getListOfAircrafts() {
this.serviceAircraft.getListOfAircrafts(true)
.subscribe((data) => {
data.sort((a, b) => a.name.localeCompare(b.name));
this.listOfAircraft = data;
this.countDatasLoaded++;
});
this.serviceAircraft.getListOfAircrafts(true).subscribe((data) => {
data.sort((a, b) => a.name.localeCompare(b.name));
this.listOfAircraft = data;
this.countDatasLoaded++;
});
}
private getListOfDropZones() {
this.serviceDropzone.getListOfDropZones(true)
.subscribe((data) => {
data.sort((a, b) => (b.isFavorite ? 1 : 0) - (a.isFavorite ? 1 : 0) || a.name.localeCompare(b.name));
this.listOfDropZone = data;
this.listOfFilteredDropZone = data;
this.countDatasLoaded++;
});
this.serviceDropzone.getListOfDropZones(true).subscribe((data) => {
data.sort(
(a, b) =>
(b.isFavorite ? 1 : 0) - (a.isFavorite ? 1 : 0) ||
a.name.localeCompare(b.name)
);
this.listOfDropZone = data;
this.listOfFilteredDropZone = data;
this.countDatasLoaded++;
});
}
private getListOfGears() {
this.serviceGear.getListOfGears()
.subscribe((data) => {
data.sort((a, b) => b.id - a.id);
this.listOfGear = data;
this.countDatasLoaded++;
});
this.serviceGear.getListOfGears().subscribe((data) => {
data.sort((a, b) => b.id - a.id);
this.listOfGear = data;
this.countDatasLoaded++;
});
}
private initForm() {
@@ -212,8 +251,8 @@ export class NewJumpComponent implements OnInit {
filterValue = event.toLowerCase();
this.listOfFilteredDropZone = this.listOfDropZone;
this.listOfFilteredDropZone = this.listOfFilteredDropZone.filter((option) =>
option.name.toLowerCase().includes(filterValue)
this.listOfFilteredDropZone = this.listOfFilteredDropZone.filter(
(option) => option.name.toLowerCase().includes(filterValue)
);
}
}
@@ -228,12 +267,12 @@ export class NewJumpComponent implements OnInit {
public resetDz() {
this.selectedDz = undefined;
this.onChangeDz('');
this.onChangeDz("");
}
private updateTitle() {
this.translateService.get("NewJump_Title").subscribe(
data => { this.serviceComm.updatedComponentTitle(data); }
);
this.translateService.get("NewJump_Title").subscribe((data) => {
this.serviceComm.updatedComponentTitle(data);
});
}
}

View File

@@ -1,66 +1,145 @@
<div class="content">
<div>
<button mat-raised-button color="accent" [routerLink]="['/tunnelFlights']" [routerLinkActive]="['active']" skipLocationChange>{{ 'NewTunnelFlight_GoToJump' | translate }}</button>
<button
mat-raised-button
color="accent"
[routerLink]="['/tunnelFlights']"
[routerLinkActive]="['active']"
skipLocationChange
>
{{ "NewTunnelFlight_GoToJump" | translate }}
</button>
</div>
<form class="formNewJumps" (ngSubmit)="onFormSubmit()" *ngIf="notLoadingToDisplay() else loading">
<form
class="formNewJumps"
(ngSubmit)="onFormSubmit()"
*ngIf="notLoadingToDisplay(); else loading"
>
<mat-form-field>
<mat-label>{{ 'NewTunnelFlight_ChooseJumpType' | translate }}</mat-label>
<input type="text" matInput [matAutocomplete]="autoJumpType" [(ngModel)]="selectedJumpType" name="selectedJumpType">
<mat-autocomplete #autoJumpType="matAutocomplete" [displayWith]="displayNameFn">
<mat-label>{{ "NewTunnelFlight_ChooseJumpType" | translate }}</mat-label>
<input
type="text"
matInput
[matAutocomplete]="autoJumpType"
[(ngModel)]="selectedJumpType"
name="selectedJumpType"
/>
<mat-autocomplete
#autoJumpType="matAutocomplete"
[displayWith]="displayNameFn"
>
<mat-option *ngFor="let jumpType of listOfJumpType" [value]="jumpType">
{{jumpType.name}}
{{ jumpType.name }}
</mat-option>
</mat-autocomplete>
<button *ngIf="selectedJumpType" matSuffix mat-icon-button aria-label="Clear" (click)="selectedJumpType=undefined">
<button
*ngIf="selectedJumpType"
matSuffix
mat-icon-button
aria-label="Clear"
(click)="selectedJumpType = undefined"
>
<mat-icon>close</mat-icon>
</button>
</mat-form-field>
<mat-form-field>
<mat-label>{{ 'NewTunnelFlight_ChooseTunnel' | translate }}</mat-label>
<input type="text" matInput [matAutocomplete]="autoDropZone" [(ngModel)]="selectedTunnel"
(ngModelChange)="onChangeTunnel($event)" name="selectedTunnel">
<mat-autocomplete #autoDropZone="matAutocomplete" [displayWith]="displayNameFn">
<mat-option *ngFor="let tunnel of listOfFilteredTunnel" [value]="tunnel">
{{tunnel.name}}
<mat-label>{{ "NewTunnelFlight_ChooseTunnel" | translate }}</mat-label>
<input
type="text"
matInput
[matAutocomplete]="autoDropZone"
[(ngModel)]="selectedTunnel"
(ngModelChange)="onChangeTunnel($event)"
name="selectedTunnel"
/>
<mat-autocomplete
#autoDropZone="matAutocomplete"
[displayWith]="displayNameFn"
>
<mat-option
*ngFor="let tunnel of listOfFilteredTunnel"
[value]="tunnel"
>
{{ tunnel.name }}
</mat-option>
</mat-autocomplete>
<button *ngIf="selectedTunnel" matSuffix mat-icon-button aria-label="Clear" (click)="resetTunnel()">
<button
*ngIf="selectedTunnel"
matSuffix
mat-icon-button
aria-label="Clear"
(click)="resetTunnel()"
>
<mat-icon>close</mat-icon>
</button>
</mat-form-field>
<mat-form-field>
<mat-label>{{ 'NewTunnelFlight_Date_Lbl' | translate }}</mat-label>
<input matInput [max]="maxDate" [matDatepicker]="flightDateDp" [(ngModel)]="flightDate" name="flightDate" disabled>
<mat-datepicker-toggle matSuffix [for]="flightDateDp"></mat-datepicker-toggle>
<mat-label>{{ "NewTunnelFlight_Date_Lbl" | translate }}</mat-label>
<input
matInput
[max]="maxDate"
[matDatepicker]="flightDateDp"
[(ngModel)]="flightDate"
name="flightDate"
disabled
/>
<mat-datepicker-toggle
matSuffix
[for]="flightDateDp"
></mat-datepicker-toggle>
<mat-datepicker #flightDateDp disabled="false"></mat-datepicker>
</mat-form-field>
<mat-form-field>
<mat-label>{{ 'NewTunnelFlight_Minutes_Lbl' | translate }}</mat-label>
<input matInput placeholder="{{ 'NewTunnelFlight_Minutes' | translate }}" [(ngModel)]="minutesOfFlight"
name="minutesOfFlight" type="number">
<button *ngIf="minutesOfFlight" matSuffix mat-icon-button aria-label="Clear" (click)="minutesOfFlight=undefined">
<mat-label>{{ "NewTunnelFlight_Minutes_Lbl" | translate }}</mat-label>
<input
matInput
placeholder="{{ 'NewTunnelFlight_Minutes' | translate }}"
[(ngModel)]="minutesOfFlight"
name="minutesOfFlight"
type="number"
/>
<button
*ngIf="minutesOfFlight"
matSuffix
mat-icon-button
aria-label="Clear"
(click)="minutesOfFlight = undefined"
>
<mat-icon>close</mat-icon>
</button>
</mat-form-field>
<mat-form-field>
<mat-label>{{ 'NewTunnelFlight_Comments_Lbl' | translate }}</mat-label>
<textarea matInput placeholder="{{ 'NewTunnelFlight_Comments' | translate }}" [(ngModel)]="comments"
name="comments" type="text"></textarea>
<button *ngIf="comments" matSuffix mat-icon-button aria-label="Clear" (click)="comments=undefined">
<mat-label>{{ "NewTunnelFlight_Comments_Lbl" | translate }}</mat-label>
<textarea
matInput
placeholder="{{ 'NewTunnelFlight_Comments' | translate }}"
[(ngModel)]="comments"
name="comments"
type="text"
></textarea>
<button
*ngIf="comments"
matSuffix
mat-icon-button
aria-label="Clear"
(click)="comments = undefined"
>
<mat-icon>close</mat-icon>
</button>
</mat-form-field>
<br />
<button mat-raised-button color="accent" *ngIf="isValidatedForm()">{{ 'NewTunnelFlight_Submit' | translate }}</button>
<button mat-raised-button color="accent" *ngIf="isValidatedForm()">
{{ "NewTunnelFlight_Submit" | translate }}
</button>
</form>
<ng-template #loading>
<mat-progress-spinner [mode]="'indeterminate'"></mat-progress-spinner>
</ng-template>
</div>
</div>

View File

@@ -1,41 +1,72 @@
import { Component, OnInit } from "@angular/core";
import { formatDate } from '@angular/common';
import { DateAdapter, MAT_DATE_FORMATS, NativeDateAdapter } from "@angular/material/core";
import { TranslateService } from '@ngx-translate/core';
import { RouterLink, RouterModule } from "@angular/router";
import { formatDate } from "@angular/common";
import {
DateAdapter,
MAT_DATE_FORMATS,
NativeDateAdapter,
} from "@angular/material/core";
import { TranslateModule, TranslateService } from "@ngx-translate/core";
import { CommonModule } from "@angular/common";
import { MatIconModule } from "@angular/material/icon";
import { MatOptionModule } from "@angular/material/core";
import { MatAutocompleteModule } from "@angular/material/autocomplete";
import { MatFormFieldModule } from "@angular/material/form-field";
import { MatDatepickerModule } from "@angular/material/datepicker";
import { MatProgressSpinnerModule } from "@angular/material/progress-spinner";
import { FormsModule, ReactiveFormsModule } from "@angular/forms";
import { MatInputModule } from "@angular/material/input";
import { MatButtonModule } from "@angular/material/button";
import { TunnelResp } from "../../models/tunnel";
import { JumpTypeResp } from "../../models/jumpType";
import { TunnelService } from '../../services/tunnel.service';
import { ServiceComm } from '../../services/service-comm.service';
import { TunnelService } from "../../services/tunnel.service";
import { ServiceComm } from "../../services/service-comm.service";
import { TunnelFlightService } from "../../services/tunnel-flight.service";
import { JumpTypeService } from "../../services/jump-type.service";
import { DateService } from "../../services/date.service";
export const PICK_FORMATS = {
parse: { dateInput: 'yy MM dd' },
parse: { dateInput: "yy MM dd" },
display: {
dateInput: 'yyyy-MM-dd',
monthYearLabel: 'yyyy MMM',
dateA11yLabel: 'yyyy MM dd',
monthYearA11yLabel: 'yyyy MMMM',
}
dateInput: "yyyy-MM-dd",
monthYearLabel: "yyyy MMM",
dateA11yLabel: "yyyy MM dd",
monthYearA11yLabel: "yyyy MMMM",
},
};
class PickDateAdapter extends NativeDateAdapter {
format(date: Date, displayFormat: Object): string {
override format(date: Date, displayFormat: Object): string {
return formatDate(date, displayFormat.toString(), "en");
}
}
@Component({
selector: 'app-new-tunnel-flight',
templateUrl: './new-tunnel-flight.component.html',
styleUrls: ['./new-tunnel-flight.component.css'],
providers: [
{ provide: DateAdapter, useClass: PickDateAdapter },
{ provide: MAT_DATE_FORMATS, useValue: PICK_FORMATS }
],
standalone: false
selector: "app-new-tunnel-flight",
templateUrl: "./new-tunnel-flight.component.html",
styleUrls: ["./new-tunnel-flight.component.css"],
providers: [
{ provide: DateAdapter, useClass: PickDateAdapter },
{ provide: MAT_DATE_FORMATS, useValue: PICK_FORMATS },
],
imports: [
TranslateModule,
CommonModule,
RouterModule,
RouterLink,
FormsModule,
MatFormFieldModule,
ReactiveFormsModule,
MatAutocompleteModule,
MatInputModule,
MatButtonModule,
MatIconModule,
MatFormFieldModule,
MatOptionModule,
MatDatepickerModule,
MatProgressSpinnerModule,
],
})
export class NewTunnelFlightComponent implements OnInit {
public flightDate: Date;
@@ -51,12 +82,14 @@ export class NewTunnelFlightComponent implements OnInit {
public listOfJumpType: Array<JumpTypeResp>;
public maxDate: Date;
constructor(private serviceComm: ServiceComm,
private serviceTunnel: TunnelService,
private serviceTunnelFlight: TunnelFlightService,
private serviceJumpType: JumpTypeService,
private translateService: TranslateService,
private dateService: DateService) { }
constructor(
private serviceComm: ServiceComm,
private serviceTunnel: TunnelService,
private serviceTunnelFlight: TunnelFlightService,
private serviceJumpType: JumpTypeService,
private translateService: TranslateService,
private dateService: DateService
) {}
ngOnInit() {
this.serviceComm.forceTranslateTitle.subscribe((data) => {
@@ -77,45 +110,48 @@ export class NewTunnelFlightComponent implements OnInit {
public onFormSubmit() {
this.pendingAddRequest = true;
this.serviceTunnelFlight.addFlight(this.selectedTunnel.id,
this.selectedJumpType.id,
this.flightDate,
this.minutesOfFlight,
this.comments)
.subscribe(() => {
this.comments = undefined;
this.serviceTunnelFlight
.addFlight(
this.selectedTunnel.id,
this.selectedJumpType.id,
this.flightDate,
this.minutesOfFlight,
this.comments
)
.subscribe(() => {
this.comments = undefined;
if (this.resetForm === true) {
this.initForm();
}
this.pendingAddRequest = false;
});
if (this.resetForm === true) {
this.initForm();
}
this.pendingAddRequest = false;
});
}
public isValidatedForm(): boolean {
return (this.selectedTunnel !== undefined &&
this.selectedTunnel.id !== undefined &&
this.minutesOfFlight !== undefined &&
typeof this.minutesOfFlight === "number");
return (
this.selectedTunnel !== undefined &&
this.selectedTunnel.id !== undefined &&
this.minutesOfFlight !== undefined &&
typeof this.minutesOfFlight === "number"
);
}
private getListOfTunnels() {
this.serviceTunnel.getListOfTunnels()
.subscribe((data) => {
data.sort((a, b) => a.name.localeCompare(b.name));
this.listOfTunnel = data;
this.listOfFilteredTunnel = data;
this.countDatasLoaded++;
});
this.serviceTunnel.getListOfTunnels().subscribe((data) => {
data.sort((a, b) => a.name.localeCompare(b.name));
this.listOfTunnel = data;
this.listOfFilteredTunnel = data;
this.countDatasLoaded++;
});
}
private getListOfJumpTypes() {
this.serviceJumpType.getListOfJumpTypesForTunnel()
.subscribe((data) => {
data.sort((a, b) => a.name.localeCompare(b.name));
this.listOfJumpType = data;
this.countDatasLoaded++;
});
this.serviceJumpType.getListOfJumpTypesForTunnel().subscribe((data) => {
data.sort((a, b) => a.name.localeCompare(b.name));
this.listOfJumpType = data;
this.countDatasLoaded++;
});
}
public notLoadingToDisplay(): boolean {
@@ -123,9 +159,9 @@ export class NewTunnelFlightComponent implements OnInit {
}
private updateTitle() {
this.translateService.get("NewTunnelFlight_Title").subscribe(
data => { this.serviceComm.updatedComponentTitle(data); }
);
this.translateService.get("NewTunnelFlight_Title").subscribe((data) => {
this.serviceComm.updatedComponentTitle(data);
});
}
private initForm() {
@@ -139,7 +175,7 @@ export class NewTunnelFlightComponent implements OnInit {
public resetTunnel() {
this.selectedTunnel = undefined;
this.onChangeTunnel('');
this.onChangeTunnel("");
}
public onChangeTunnel(event: any) {

View File

@@ -21,8 +21,8 @@
aria-label="Force the refresh of the stats"
style="cursor: pointer"
(click)="refreshStats()"
>cached</mat-icon
>
svgIcon="reset"
></mat-icon>
</div>
<mat-tab-group

View File

@@ -1,9 +1,12 @@
import { Component, OnInit, ViewChild } from "@angular/core";
import { MatTableDataSource } from "@angular/material/table";
import { MatTableDataSource, MatTableModule } from "@angular/material/table";
import { MatTabChangeEvent, MatTabGroup } from "@angular/material/tabs";
import { TranslateService } from "@ngx-translate/core";
import { TranslateModule, TranslateService } from "@ngx-translate/core";
import { DatePipe } from "@angular/common";
import { ChartConfiguration, ChartData, ChartType, Colors } from "chart.js";
import { ChartConfiguration, ChartData, ChartType } from "chart.js";
import { BaseChartDirective } from "ng2-charts";
import { MatTabsModule } from "@angular/material/tabs";
import { MatIconModule } from "@angular/material/icon";
import { ServiceComm } from "../../services/service-comm.service";
import { StatsService } from "../../services/stats.service";
@@ -19,7 +22,13 @@ import {
selector: "app-summary",
templateUrl: "./summary.component.html",
styleUrls: ["./summary.component.css"],
standalone: false,
imports: [
MatTabsModule,
TranslateModule,
BaseChartDirective,
MatIconModule,
MatTableModule,
],
})
export class SummaryComponent implements OnInit {
public dsNbJumpByDz: MatTableDataSource<StatsByDzResp>;
@@ -39,7 +48,7 @@ export class SummaryComponent implements OnInit {
];
public barChartLegend = true;
public barChartPlugins = [];
public barChartPlugins: any = [];
public barChartData: ChartData<"line">;
public barChartOptions: ChartConfiguration["options"];
public barChartType: ChartType;
@@ -53,7 +62,7 @@ export class SummaryComponent implements OnInit {
constructor(
private serviceApi: StatsService,
private serviceComm: ServiceComm,
private translateService: TranslateService
private translateService: TranslateService,
) {}
ngOnInit() {
@@ -71,7 +80,7 @@ export class SummaryComponent implements OnInit {
const datepipe: DatePipe = new DatePipe("en-US");
let formattedDate = datepipe.transform(
data.lastJump.jumpDate,
"EEEE dd MMMM YYYY"
"EEEE dd MMMM YYYY",
);
this.lastJump = formattedDate + " (" + data.lastJump.dropZone.name + ")";
});
@@ -81,7 +90,7 @@ export class SummaryComponent implements OnInit {
this.dsJumpForLastMonthByDz = new MatTableDataSource(data.byDz);
data.byJumpType.sort((a, b) => b.nb - a.nb);
this.dsJumpForLastMonthByJumpType = new MatTableDataSource(
data.byJumpType
data.byJumpType,
);
});
@@ -96,106 +105,28 @@ export class SummaryComponent implements OnInit {
public onTabChanged(event: MatTabChangeEvent) {
switch (event.index) {
case 0:
this.serviceApi.getStatsOfLastMonth().subscribe((data) => {
data.byDz.sort((a, b) => b.nb - a.nb);
this.dsJumpForLastMonthByDz = new MatTableDataSource(data.byDz);
data.byJumpType.sort((a, b) => b.nb - a.nb);
this.dsJumpForLastMonthByJumpType = new MatTableDataSource(
data.byJumpType
);
});
this.statsLastMonth();
break;
case 1:
this.serviceApi.getStatsOfLastYear().subscribe((data) => {
data.byDz.sort((a, b) => b.nb - a.nb);
this.dsJumpForLastYearByDz = new MatTableDataSource(data.byDz);
data.byJumpType.sort((a, b) => b.nb - a.nb);
this.dsJumpForLastYearByJumpType = new MatTableDataSource(
data.byJumpType
);
});
this.statsLastYear();
break;
case 2:
this.serviceApi.getStatsByDz().subscribe((data) => {
data.sort((a, b) => b.nb - a.nb);
this.dsNbJumpByDz = new MatTableDataSource(data);
});
this.statsByDz();
break;
case 3:
this.serviceApi.getStatsByAircraft().subscribe((data) => {
data.sort((a, b) => b.nb - a.nb);
this.dsNbJumpByAircraft = new MatTableDataSource(data);
});
this.statsByAircraft();
break;
case 4:
this.serviceApi.getStatsByGear().subscribe((data) => {
data.sort((a, b) => b.nb - a.nb);
this.dsNbJumpByGear = new MatTableDataSource(data);
});
this.statsByGear();
break;
case 5:
this.serviceApi.getStatsByJumpType().subscribe((data) => {
data.sort((a, b) => b.nb - a.nb);
this.dsNbJumpByType = new MatTableDataSource(data);
});
this.statsByJumpType();
break;
case 6:
this.serviceApi.getStatsByYear().subscribe((data) => {
data.sort((a, b) => b.label.localeCompare(a.label));
this.dsNbJumpByYear = new MatTableDataSource(data);
});
this.statsByYear();
break;
case 7:
this.serviceApi.getStatsByYearByJumpType().subscribe((data) => {
data.sort((a, b) => a.label.localeCompare(b.label));
let firstYear: number = Number(data[0].label);
const now = new Date();
const currentYear = now.getFullYear();
const nbYears = currentYear - firstYear;
let listOfYears = new Array(nbYears)
.fill(null)
.map(() => firstYear++);
// Prepare the list of jump type with am empty array
let tmpResults = new Map<string, number[]>();
const listOfJumpType = [...new Set(data.map((obj) => obj.label2))];
listOfJumpType.forEach((type) => {
tmpResults.set(type, new Array(nbYears).fill(NaN));
});
for (let i = 0; i < listOfYears.length; i++) {
const year = listOfYears[i].toString();
let filteredStats = data.filter((d) => d.label == year);
if (filteredStats.length > 0) {
filteredStats.forEach((fs) => {
tmpResults.get(fs.label2)[i] = fs.nb;
});
}
}
const results = [];
tmpResults.forEach((value, key) => {
const color = this.jumpTypeToColor.get(key);
let tmp = {
label: key,
data: value,
backgroundColor: color,
borderColor: color,
pointBackgroundColor: color,
fill: false,
pointRadius: 6,
};
results.push(tmp);
});
this.barChartData = {
labels: listOfYears,
datasets: results,
};
});
this.statsByYearByJumpType();
break;
}
}
@@ -216,7 +147,7 @@ export class SummaryComponent implements OnInit {
display: true,
},
colors: {
forceOverride: true,
forceOverride: false,
},
},
interaction: {
@@ -252,4 +183,122 @@ export class SummaryComponent implements OnInit {
["Wingsuit 3", "#9400D3"],
]);
}
// #region Private methods to get stats
private statsByYearByJumpType() {
this.serviceApi.getStatsByYearByJumpType().subscribe((data) => {
data.sort((a, b) => a.label.localeCompare(b.label));
let firstYear: number = Number(data[0].label);
const now = new Date();
const currentYear = now.getFullYear();
const nbYears = currentYear - firstYear;
let listOfYears = new Array(nbYears).fill(null).map(() => firstYear++);
// Prepare the list of jump type with am empty array
let tmpResults = new Map<string, number[]>();
const listOfJumpType = [...new Set(data.map((obj) => obj.label2))];
listOfJumpType.forEach((type) => {
tmpResults.set(type, new Array(nbYears).fill(NaN));
});
for (let i = 0; i < listOfYears.length; i++) {
const year = listOfYears[i].toString();
let filteredStats = data.filter((d) => d.label == year);
if (filteredStats.length > 0) {
filteredStats.forEach((fs) => {
tmpResults.get(fs.label2)[i] = fs.nb;
});
}
}
const results: {
label: string;
data: number[];
backgroundColor: string;
borderColor: string;
pointBackgroundColor: string;
fill: boolean;
pointRadius: number;
}[] = [];
tmpResults.forEach((value, key) => {
const color = this.jumpTypeToColor.get(key);
let tmp = {
label: key,
data: value,
backgroundColor: color,
borderColor: color,
pointBackgroundColor: color,
fill: false,
pointRadius: 6,
};
results.push(tmp);
});
this.barChartData = {
labels: listOfYears,
datasets: results,
};
});
}
private statsByYear() {
this.serviceApi.getStatsByYear().subscribe((data) => {
data.sort((a, b) => b.label.localeCompare(a.label));
this.dsNbJumpByYear = new MatTableDataSource(data);
});
}
private statsByJumpType() {
this.serviceApi.getStatsByJumpType().subscribe((data) => {
data.sort((a, b) => b.nb - a.nb);
this.dsNbJumpByType = new MatTableDataSource(data);
});
}
private statsByGear() {
this.serviceApi.getStatsByGear().subscribe((data) => {
data.sort((a, b) => b.nb - a.nb);
this.dsNbJumpByGear = new MatTableDataSource(data);
});
}
private statsByAircraft() {
this.serviceApi.getStatsByAircraft().subscribe((data) => {
data.sort((a, b) => b.nb - a.nb);
this.dsNbJumpByAircraft = new MatTableDataSource(data);
});
}
private statsByDz() {
this.serviceApi.getStatsByDz().subscribe((data) => {
data.sort((a, b) => b.nb - a.nb);
this.dsNbJumpByDz = new MatTableDataSource(data);
});
}
private statsLastYear() {
this.serviceApi.getStatsOfLastYear().subscribe((data) => {
data.byDz.sort((a, b) => b.nb - a.nb);
this.dsJumpForLastYearByDz = new MatTableDataSource(data.byDz);
data.byJumpType.sort((a, b) => b.nb - a.nb);
this.dsJumpForLastYearByJumpType = new MatTableDataSource(
data.byJumpType,
);
});
}
private statsLastMonth() {
this.serviceApi.getStatsOfLastMonth().subscribe((data) => {
data.byDz.sort((a, b) => b.nb - a.nb);
this.dsJumpForLastMonthByDz = new MatTableDataSource(data.byDz);
data.byJumpType.sort((a, b) => b.nb - a.nb);
this.dsJumpForLastMonthByJumpType = new MatTableDataSource(
data.byJumpType,
);
});
}
//#endregion
}

View File

@@ -26,7 +26,7 @@
<input matInput type="text" formControlName="email" />
</mat-form-field>
</p>
<p>
<mat-form-field>
<mat-label>Current password</mat-label>
@@ -39,8 +39,10 @@
<input matInput type="text" formControlName="newPassword" />
</mat-form-field>
</p>
<button type="submit" mat-raised-button color="accent">Update my profile</button>
<button type="submit" mat-raised-button color="accent">
Update my profile
</button>
</form>
</fieldset>

View File

@@ -1,20 +1,54 @@
import { Component, OnInit } from '@angular/core';
import { FormGroup, FormControl, Validators } from '@angular/forms';
import { AuthenticationService } from '../../services/authentication.service';
import { User } from '../../models/user';
import { Component, OnInit } from "@angular/core";
import {
FormGroup,
FormControl,
Validators,
ReactiveFormsModule,
} from "@angular/forms";
import { MatLabel, MatFormFieldModule } from "@angular/material/form-field";
import {
TranslateModule,
TranslatePipe,
TranslateService,
} from "@ngx-translate/core";
import { MatInputModule } from "@angular/material/input";
import { MatButtonModule } from "@angular/material/button";
import { AuthenticationService } from "../../services/authentication.service";
import { ListOfImagesComponent } from "../list-of-images/list-of-images.component";
import { ServiceComm } from "../../services/service-comm.service";
@Component({
selector: 'app-user-profile',
templateUrl: './user-profile.component.html',
styleUrls: ['./user-profile.component.css'],
standalone: false
selector: "app-user-profile",
templateUrl: "./user-profile.component.html",
styleUrls: ["./user-profile.component.css"],
imports: [
ListOfImagesComponent,
ReactiveFormsModule,
MatLabel,
MatFormFieldModule,
MatInputModule,
MatButtonModule,
TranslateModule,
],
})
export class UserProfileComponent implements OnInit {
public userForm: FormGroup;
constructor(private authenticationService: AuthenticationService) { }
constructor(
private authenticationService: AuthenticationService,
private translateService: TranslateService,
private serviceComm: ServiceComm,
) {}
ngOnInit() {
this.serviceComm.forceTranslateTitle.subscribe((data) => {
if (data === true) {
this.updateTitle();
}
});
this.updateTitle();
ngOnInit(): void {
const currentUser = this.authenticationService.currentUserValue;
this.userForm = new FormGroup(
@@ -33,15 +67,15 @@ export class UserProfileComponent implements OnInit {
Validators.email,
]),
currentPassword: new FormControl(
'',
Validators.pattern('^[A-Za-z0-9_-]{8,15}$')
"",
Validators.pattern("^[A-Za-z0-9_-]{8,15}$"),
),
newPassword: new FormControl(
'',
Validators.pattern('^[A-Za-z0-9_-]{8,15}$')
"",
Validators.pattern("^[A-Za-z0-9_-]{8,15}$"),
),
},
{ updateOn: 'blur' }
{ updateOn: "blur" },
);
}
@@ -60,56 +94,62 @@ export class UserProfileComponent implements OnInit {
private calculTailleVoile() {
let tailleVoile = new Map<string, Array<number>>();
tailleVoile.set("60", [ 175, 161, 147, 133, 124, 115, 107, 97, 89 ]);
tailleVoile.set("61", [ 178, 163, 149, 135, 126, 116, 108, 98, 90 ]);
tailleVoile.set("62", [ 180, 166, 151, 137, 127, 118, 109, 99, 91 ]);
tailleVoile.set("63", [ 183, 168, 153, 138, 129, 119, 111, 100, 91 ]);
tailleVoile.set("64", [ 185, 170, 155, 140, 130, 121, 112, 101, 92 ]);
tailleVoile.set("65", [ 188, 173, 157, 142, 132, 122, 113, 102, 93 ]);
tailleVoile.set("66", [ 190, 175, 159, 144, 134, 123, 114, 103, 94 ]);
tailleVoile.set("67", [ 193, 177, 161, 146, 135, 125, 116, 104, 94 ]);
tailleVoile.set("68", [ 195, 179, 164, 147, 137, 126, 117, 105, 95 ]);
tailleVoile.set("69", [ 198, 182, 166, 149, 138, 128, 118, 106, 96 ]);
tailleVoile.set("70", [ 200, 184, 168, 151, 140, 129, 119, 107, 96 ]);
tailleVoile.set("71", [ 203, 186, 170, 153, 142, 130, 120, 107, 97 ]);
tailleVoile.set("72", [ 205, 189, 172, 155, 143, 132, 121, 108, 98 ]);
tailleVoile.set("73", [ 208, 191, 174, 156, 145, 133, 123, 109, 99 ]);
tailleVoile.set("74", [ 210, 193, 176, 158, 146, 134, 124, 110, 99 ]);
tailleVoile.set("75", [ 213, 196, 178, 160, 148, 136, 125, 111, 100 ]);
tailleVoile.set("76", [ 215, 198, 180, 162, 150, 137, 126, 112, 101 ]);
tailleVoile.set("77", [ 218, 200, 182, 163, 151, 139, 127, 113, 101 ]);
tailleVoile.set("78", [ 220, 202, 184, 165, 153, 140, 128, 114, 102 ]);
tailleVoile.set("79", [ 223, 205, 186, 167, 154, 141, 129, 115, 103 ]);
tailleVoile.set("80", [ 225, 207, 188, 169, 156, 143, 131, 115, 103 ]);
tailleVoile.set("81", [ 228, 209, 190, 170, 157, 144, 132, 116, 104 ]);
tailleVoile.set("82", [ 230, 212, 192, 172, 159, 145, 133, 117, 104 ]);
tailleVoile.set("83", [ 233, 214, 194, 174, 160, 146, 134, 118, 105 ]);
tailleVoile.set("84", [ 235, 216, 196, 176, 162, 148, 135, 119, 106 ]);
tailleVoile.set("85", [ 238, 219, 198, 177, 163, 149, 136, 120, 106 ]);
tailleVoile.set("86", [ 240, 221, 201, 179, 165, 150, 137, 120, 107 ]);
tailleVoile.set("87", [ 243, 223, 203, 181, 166, 152, 138, 121, 108 ]);
tailleVoile.set("88", [ 245, 225, 205, 183, 168, 153, 139, 122, 108 ]);
tailleVoile.set("89", [ 248, 228, 207, 184, 170, 154, 140, 123, 109 ]);
tailleVoile.set("90", [ 250, 230, 209, 186, 171, 156, 141, 124, 109 ]);
tailleVoile.set("91", [ 253, 232, 211, 188, 173, 157, 143, 124, 110 ]);
tailleVoile.set("92", [ 255, 235, 213, 190, 174, 158, 144, 125, 110 ]);
tailleVoile.set("93", [ 258, 237, 215, 191, 176, 159, 145, 126, 111 ]);
tailleVoile.set("94", [ 260, 239, 217, 193, 177, 161, 146, 127, 112 ]);
tailleVoile.set("95", [ 263, 242, 219, 195, 179, 162, 147, 128, 112 ]);
tailleVoile.set("96", [ 265, 244, 221, 197, 180, 163, 148, 128, 113 ]);
tailleVoile.set("97", [ 268, 246, 223, 198, 182, 164, 149, 129, 113 ]);
tailleVoile.set("98", [ 270, 248, 225, 200, 183, 166, 150, 130, 114 ]);
tailleVoile.set("99", [ 273, 251, 227, 202, 185, 167, 151, 131, 114 ]);
tailleVoile.set("100", [ 275, 253, 229, 203, 186, 168, 152, 131, 115 ]);
tailleVoile.set("101", [ 278, 255, 231, 205, 188, 169, 153, 132, 115 ]);
tailleVoile.set("102", [ 280, 258, 233, 207, 189, 171, 154, 133, 116 ]);
tailleVoile.set("103", [ 283, 260, 235, 209, 190, 172, 155, 134, 116 ]);
tailleVoile.set("104", [ 285, 262, 237, 210, 192, 173, 156, 134, 117 ]);
tailleVoile.set("105", [ 288, 265, 239, 212, 193, 174, 157, 135, 118 ]);
tailleVoile.set("106", [ 290, 267, 241, 214, 195, 175, 158, 136, 118 ]);
tailleVoile.set("107", [ 293, 269, 243, 215, 196, 177, 159, 136, 119 ]);
tailleVoile.set("108", [ 295, 271, 245, 217, 198, 178, 160, 137, 119 ]);
tailleVoile.set("109", [ 298, 274, 247, 219, 199, 179, 161, 138, 120 ]);
tailleVoile.set("110", [ 300, 276, 249, 220, 201, 180, 162, 138, 120 ]);
tailleVoile.set("60", [175, 161, 147, 133, 124, 115, 107, 97, 89]);
tailleVoile.set("61", [178, 163, 149, 135, 126, 116, 108, 98, 90]);
tailleVoile.set("62", [180, 166, 151, 137, 127, 118, 109, 99, 91]);
tailleVoile.set("63", [183, 168, 153, 138, 129, 119, 111, 100, 91]);
tailleVoile.set("64", [185, 170, 155, 140, 130, 121, 112, 101, 92]);
tailleVoile.set("65", [188, 173, 157, 142, 132, 122, 113, 102, 93]);
tailleVoile.set("66", [190, 175, 159, 144, 134, 123, 114, 103, 94]);
tailleVoile.set("67", [193, 177, 161, 146, 135, 125, 116, 104, 94]);
tailleVoile.set("68", [195, 179, 164, 147, 137, 126, 117, 105, 95]);
tailleVoile.set("69", [198, 182, 166, 149, 138, 128, 118, 106, 96]);
tailleVoile.set("70", [200, 184, 168, 151, 140, 129, 119, 107, 96]);
tailleVoile.set("71", [203, 186, 170, 153, 142, 130, 120, 107, 97]);
tailleVoile.set("72", [205, 189, 172, 155, 143, 132, 121, 108, 98]);
tailleVoile.set("73", [208, 191, 174, 156, 145, 133, 123, 109, 99]);
tailleVoile.set("74", [210, 193, 176, 158, 146, 134, 124, 110, 99]);
tailleVoile.set("75", [213, 196, 178, 160, 148, 136, 125, 111, 100]);
tailleVoile.set("76", [215, 198, 180, 162, 150, 137, 126, 112, 101]);
tailleVoile.set("77", [218, 200, 182, 163, 151, 139, 127, 113, 101]);
tailleVoile.set("78", [220, 202, 184, 165, 153, 140, 128, 114, 102]);
tailleVoile.set("79", [223, 205, 186, 167, 154, 141, 129, 115, 103]);
tailleVoile.set("80", [225, 207, 188, 169, 156, 143, 131, 115, 103]);
tailleVoile.set("81", [228, 209, 190, 170, 157, 144, 132, 116, 104]);
tailleVoile.set("82", [230, 212, 192, 172, 159, 145, 133, 117, 104]);
tailleVoile.set("83", [233, 214, 194, 174, 160, 146, 134, 118, 105]);
tailleVoile.set("84", [235, 216, 196, 176, 162, 148, 135, 119, 106]);
tailleVoile.set("85", [238, 219, 198, 177, 163, 149, 136, 120, 106]);
tailleVoile.set("86", [240, 221, 201, 179, 165, 150, 137, 120, 107]);
tailleVoile.set("87", [243, 223, 203, 181, 166, 152, 138, 121, 108]);
tailleVoile.set("88", [245, 225, 205, 183, 168, 153, 139, 122, 108]);
tailleVoile.set("89", [248, 228, 207, 184, 170, 154, 140, 123, 109]);
tailleVoile.set("90", [250, 230, 209, 186, 171, 156, 141, 124, 109]);
tailleVoile.set("91", [253, 232, 211, 188, 173, 157, 143, 124, 110]);
tailleVoile.set("92", [255, 235, 213, 190, 174, 158, 144, 125, 110]);
tailleVoile.set("93", [258, 237, 215, 191, 176, 159, 145, 126, 111]);
tailleVoile.set("94", [260, 239, 217, 193, 177, 161, 146, 127, 112]);
tailleVoile.set("95", [263, 242, 219, 195, 179, 162, 147, 128, 112]);
tailleVoile.set("96", [265, 244, 221, 197, 180, 163, 148, 128, 113]);
tailleVoile.set("97", [268, 246, 223, 198, 182, 164, 149, 129, 113]);
tailleVoile.set("98", [270, 248, 225, 200, 183, 166, 150, 130, 114]);
tailleVoile.set("99", [273, 251, 227, 202, 185, 167, 151, 131, 114]);
tailleVoile.set("100", [275, 253, 229, 203, 186, 168, 152, 131, 115]);
tailleVoile.set("101", [278, 255, 231, 205, 188, 169, 153, 132, 115]);
tailleVoile.set("102", [280, 258, 233, 207, 189, 171, 154, 133, 116]);
tailleVoile.set("103", [283, 260, 235, 209, 190, 172, 155, 134, 116]);
tailleVoile.set("104", [285, 262, 237, 210, 192, 173, 156, 134, 117]);
tailleVoile.set("105", [288, 265, 239, 212, 193, 174, 157, 135, 118]);
tailleVoile.set("106", [290, 267, 241, 214, 195, 175, 158, 136, 118]);
tailleVoile.set("107", [293, 269, 243, 215, 196, 177, 159, 136, 119]);
tailleVoile.set("108", [295, 271, 245, 217, 198, 178, 160, 137, 119]);
tailleVoile.set("109", [298, 274, 247, 219, 199, 179, 161, 138, 120]);
tailleVoile.set("110", [300, 276, 249, 220, 201, 180, 162, 138, 120]);
}
private updateTitle() {
this.translateService.get("UserProfile_Title").subscribe((data) => {
this.serviceComm.updatedComponentTitle(data);
});
}
}