From 93485efc5c361bad9aaadd59d4e2eeee6e71d40e Mon Sep 17 00:00:00 2001 From: sandre Date: Tue, 13 Jan 2026 11:12:51 +0100 Subject: [PATCH] First try --- Front/skydivelogs-app/angular.json | 136 ++---- Front/skydivelogs-app/package-lock.json | 16 +- Front/skydivelogs-app/package.json | 2 +- Front/skydivelogs-app/src/app/app.module.ts | 10 +- .../create-user/create-user.component.html | 106 +++-- .../app/create-user/create-user.component.ts | 62 +-- .../list-of-tunnel-flights.component.html | 1 - .../list-of-tunnel-flights.component.ts | 444 +++++++++--------- .../app/login-user/login-user.component.html | 48 +- .../app/login-user/login-user.component.ts | 71 +-- .../src/services/request-cache.service.ts | 14 +- Front/skydivelogs-app/src/tsconfig.app.json | 12 - Front/skydivelogs-app/src/tsconfig.spec.json | 19 - Front/skydivelogs-app/tsconfig.app.json | 15 + Front/skydivelogs-app/tsconfig.json | 40 +- Front/skydivelogs-app/tsconfig.spec.json | 15 + 16 files changed, 525 insertions(+), 486 deletions(-) delete mode 100644 Front/skydivelogs-app/src/tsconfig.app.json delete mode 100644 Front/skydivelogs-app/src/tsconfig.spec.json create mode 100644 Front/skydivelogs-app/tsconfig.app.json create mode 100644 Front/skydivelogs-app/tsconfig.spec.json diff --git a/Front/skydivelogs-app/angular.json b/Front/skydivelogs-app/angular.json index 80b4071..7a8ddc1 100644 --- a/Front/skydivelogs-app/angular.json +++ b/Front/skydivelogs-app/angular.json @@ -4,25 +4,33 @@ "newProjectRoot": "projects", "projects": { "skydivelogs-app": { + "projectType": "application", + "schematics": { + "@schematics/angular:component": { + "style": "scss" + } + }, "root": "", "sourceRoot": "src", - "projectType": "application", + "prefix": "app", "architect": { "build": { "builder": "@angular-devkit/build-angular:application", "options": { - "outputPath": { - "base": "dist" - }, + "outputPath": "dist", "index": "src/index.html", - "tsConfig": "src/tsconfig.app.json", - "polyfills": [ - "src/polyfills.ts" - ], + "browser": "src/main.ts", + "polyfills": ["zone.js"], + "tsConfig": "tsconfig.app.json", + "inlineStyleLanguage": "scss", "assets": [ "src/assets", "src/config", - "src/favicon.ico" + "src/favicon.ico", + { + "glob": "**/*", + "input": "public" + } ], "styles": [ "src/assets/css/styles-app-loading.scss", @@ -30,26 +38,28 @@ "src/assets/css/new-theme.scss", "@angular/material/prebuilt-themes/pink-bluegrey.css" ], - "scripts": [], - "extractLicenses": false, - "sourceMap": true, - "optimization": false, - "namedChunks": true, - "browser": "src/main.ts" + "scripts": [] }, "configurations": { "production": { "budgets": [ + { + "type": "initial", + "maximumWarning": "500kB", + "maximumError": "1MB" + }, { "type": "anyComponentStyle", - "maximumWarning": "6kb" + "maximumWarning": "4kB", + "maximumError": "8kB" } ], - "optimization": true, - "outputHashing": "all", - "sourceMap": false, - "namedChunks": false, - "extractLicenses": true, + "outputHashing": "all" + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true, "fileReplacements": [ { "replace": "src/environments/environment.ts", @@ -57,91 +67,41 @@ } ] } - } + }, + "defaultConfiguration": "production" }, "serve": { "builder": "@angular-devkit/build-angular:dev-server", - "options": { - "buildTarget": "skydivelogs-app:build" - }, "configurations": { "production": { "buildTarget": "skydivelogs-app:build:production" + }, + "development": { + "buildTarget": "skydivelogs-app:build:development" } - } + }, + "defaultConfiguration": "development" }, "extract-i18n": { - "builder": "@angular-devkit/build-angular:extract-i18n", - "options": { - "buildTarget": "skydivelogs-app:build" - } + "builder": "@angular-devkit/build-angular:extract-i18n" }, "test": { "builder": "@angular-devkit/build-angular:karma", "options": { - "main": "src/test.ts", - "karmaConfig": "./karma.conf.js", - "polyfills": "src/polyfills.ts", - "tsConfig": "src/tsconfig.spec.json", - "scripts": [], - "styles": [ - "src/styles.css" - ], + "polyfills": ["zone.js", "zone.js/testing"], + "tsConfig": "tsconfig.spec.json", + "inlineStyleLanguage": "scss", "assets": [ - "src/assets", - "src/favicon.ico" - ] - } - }, - "lint": { - "builder": "@angular-devkit/build-angular:tslint", - "options": { - "tsConfig": [ - "src/tsconfig.app.json", - "src/tsconfig.spec.json" + { + "glob": "**/*", + "input": "public" + } ], - "exclude": [ - "**/node_modules/**" - ] - } - } - } - }, - "skydivelogs-app-e2e": { - "root": "e2e", - "sourceRoot": "e2e", - "projectType": "application", - "architect": { - "e2e": { - "builder": "@angular-devkit/build-angular:protractor", - "options": { - "protractorConfig": "./protractor.conf.js", - "devServerTarget": "skydivelogs-app:serve" - } - }, - "lint": { - "builder": "@angular-devkit/build-angular:tslint", - "options": { - "tsConfig": [ - "e2e/tsconfig.e2e.json" - ], - "exclude": [ - "**/node_modules/**" - ] + "styles": ["src/styles.scss"], + "scripts": [] } } } } - }, - "schematics": { - "@schematics/angular:component": { - "prefix": "app" - }, - "@schematics/angular:directive": { - "prefix": "app" - } - }, - "cli": { - "analytics": false } } diff --git a/Front/skydivelogs-app/package-lock.json b/Front/skydivelogs-app/package-lock.json index 46c223f..cbe4742 100644 --- a/Front/skydivelogs-app/package-lock.json +++ b/Front/skydivelogs-app/package-lock.json @@ -22,7 +22,7 @@ "@ngx-translate/core": "^15.0.0", "@ngx-translate/http-loader": "^8.0.0", "chart.js": "^4.3.0", - "ng2-charts": "^5.0.2", + "ng2-charts": "^8.0.0", "rxjs": "~7.8.0", "tslib": "^2.3.0", "zone.js": "~0.15.0" @@ -10714,19 +10714,19 @@ "license": "MIT" }, "node_modules/ng2-charts": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/ng2-charts/-/ng2-charts-5.0.4.tgz", - "integrity": "sha512-AnOZ2KSRw7QjiMMNtXz9tdnO+XrIKP/2MX1TfqEEo2fwFU5c8LFJIYqmkMPkIzAEm/U9y/1psA5TDNmxxjEdgA==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/ng2-charts/-/ng2-charts-8.0.0.tgz", + "integrity": "sha512-nofsNHI2Zt+EAwT+BJBVg0kgOhNo9ukO4CxULlaIi7VwZSr7I1km38kWSoU41Oq6os6qqIh5srnL+CcV+RFPFA==", "license": "MIT", "dependencies": { "lodash-es": "^4.17.15", "tslib": "^2.3.0" }, "peerDependencies": { - "@angular/cdk": ">=16.0.0", - "@angular/common": ">=16.0.0", - "@angular/core": ">=16.0.0", - "@angular/platform-browser": ">=16.0.0", + "@angular/cdk": ">=19.0.0", + "@angular/common": ">=19.0.0", + "@angular/core": ">=19.0.0", + "@angular/platform-browser": ">=19.0.0", "chart.js": "^3.4.0 || ^4.0.0", "rxjs": "^6.5.3 || ^7.4.0" } diff --git a/Front/skydivelogs-app/package.json b/Front/skydivelogs-app/package.json index 3ed2027..fa21033 100644 --- a/Front/skydivelogs-app/package.json +++ b/Front/skydivelogs-app/package.json @@ -25,7 +25,7 @@ "@ngx-translate/core": "^15.0.0", "@ngx-translate/http-loader": "^8.0.0", "chart.js": "^4.3.0", - "ng2-charts": "^5.0.2", + "ng2-charts": "^8.0.0", "rxjs": "~7.8.0", "tslib": "^2.3.0", "zone.js": "~0.15.0" diff --git a/Front/skydivelogs-app/src/app/app.module.ts b/Front/skydivelogs-app/src/app/app.module.ts index 9afb822..76e79d2 100644 --- a/Front/skydivelogs-app/src/app/app.module.ts +++ b/Front/skydivelogs-app/src/app/app.module.ts @@ -72,7 +72,7 @@ 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 { provideCharts, withDefaultRegisterables } from "ng2-charts"; import { JwtAuthInterceptor } from "../interceptor/jwt-auth.interceptor"; import { ErrorInterceptor } from "../interceptor/error.interceptor"; @@ -209,7 +209,6 @@ export function initConfig(configService: ConfigurationHelper) { MatSidenavModule, MatListModule, MatToolbarModule, - NgChartsModule, ], exports: [HttpClientModule], providers: [ @@ -229,11 +228,12 @@ export function initConfig(configService: ConfigurationHelper) { DatePipe, ServiceCacheApi, provideAppInitializer(() => { - const initializerFn = (initConfig)(inject(ConfigurationHelper)); - return initializerFn(); - }), + const initializerFn = initConfig(inject(ConfigurationHelper)); + return initializerFn(); + }), { provide: HTTP_INTERCEPTORS, useClass: JwtAuthInterceptor, multi: true }, { provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true }, + provideCharts(withDefaultRegisterables()), ], bootstrap: [AppComponent], }) diff --git a/Front/skydivelogs-app/src/app/create-user/create-user.component.html b/Front/skydivelogs-app/src/app/create-user/create-user.component.html index 9628b7c..af353aa 100644 --- a/Front/skydivelogs-app/src/app/create-user/create-user.component.html +++ b/Front/skydivelogs-app/src/app/create-user/create-user.component.html @@ -1,72 +1,102 @@ -
+

- {{ 'LoginCreateUser_Firstname' | translate }} - - - {{ 'LoginCreateUser_FirstnameRequired' | translate }} + {{ "LoginCreateUser_Firstname" | translate }} + + + {{ "LoginCreateUser_FirstnameRequired" | translate }} - - {{ 'LoginCreateUser_FirstnamePattern' | translate }} + + {{ "LoginCreateUser_FirstnamePattern" | translate }}

- {{ 'LoginCreateUser_Lastname' | translate }} - - - {{ 'LoginCreateUser_LastnameRequired' | translate }} + {{ "LoginCreateUser_Lastname" | translate }} + + + {{ "LoginCreateUser_LastnameRequired" | translate }} - - {{ 'LoginCreateUser_LastnamePattern' | translate }} + + {{ "LoginCreateUser_LastnamePattern" | translate }}

- {{ 'LoginCreateUser_Email' | translate }} - - - {{ 'LoginCreateUser_EmailRequired' | translate }} + {{ "LoginCreateUser_Email" | translate }} + + + {{ "LoginCreateUser_EmailRequired" | translate }} - - {{ 'LoginCreateUser_EmailPattern' | translate }} + + {{ "LoginCreateUser_EmailPattern" | translate }}

- {{ 'LoginCreateUser_Username' | translate }} - - - {{ 'LoginCreateUser_UsernameRequired' | translate }} + {{ "LoginCreateUser_Username" | translate }} + + + {{ "LoginCreateUser_UsernameRequired" | translate }} - - {{ 'LoginCreateUser_UsernamePattern' | translate }} + + {{ "LoginCreateUser_UsernamePattern" | translate }}

- {{ 'LoginCreateUser_Password' | translate }} - - - {{ 'LoginCreateUser_PasswordRequired' | translate }} + {{ "LoginCreateUser_Password" | translate }} + + + {{ "LoginCreateUser_PasswordRequired" | translate }} - - {{ 'LoginCreateUser_PasswordPattern' | translate }} + + {{ "LoginCreateUser_PasswordPattern" | translate }}

-
{{error}}
+
{{ error }}
diff --git a/Front/skydivelogs-app/src/app/create-user/create-user.component.ts b/Front/skydivelogs-app/src/app/create-user/create-user.component.ts index cef19b9..f64e9f6 100644 --- a/Front/skydivelogs-app/src/app/create-user/create-user.component.ts +++ b/Front/skydivelogs-app/src/app/create-user/create-user.component.ts @@ -9,10 +9,10 @@ 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"], + standalone: false, }) export class CreateUserComponent implements OnInit { createForm: FormGroup; @@ -21,11 +21,13 @@ export class CreateUserComponent implements OnInit { returnUrl: string; error = ""; - 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(["/"]); @@ -38,11 +40,16 @@ export class CreateUserComponent implements OnInit { 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]] + email: ["", [Validators.required, Validators.email]], }, { updateOn: "blur" } ); @@ -65,23 +72,24 @@ export class CreateUserComponent implements OnInit { } 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.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; - } - ); + this.authenticationService + .create(createUser) + .pipe(first()) + .subscribe( + (data) => { + this.router.navigate([this.returnUrl]); + }, + (error) => { + this.error = error; + this.invalidForm = false; + } + ); } } diff --git a/Front/skydivelogs-app/src/app/list-of-tunnel-flights/list-of-tunnel-flights.component.html b/Front/skydivelogs-app/src/app/list-of-tunnel-flights/list-of-tunnel-flights.component.html index 8d54f86..ec76169 100644 --- a/Front/skydivelogs-app/src/app/list-of-tunnel-flights/list-of-tunnel-flights.component.html +++ b/Front/skydivelogs-app/src/app/list-of-tunnel-flights/list-of-tunnel-flights.component.html @@ -42,7 +42,6 @@ baseChart [data]="barChartData" [options]="barChartOptions" - [plugins]="barChartPlugins" [legend]="barChartLegend" [type]="barChartType" > diff --git a/Front/skydivelogs-app/src/app/list-of-tunnel-flights/list-of-tunnel-flights.component.ts b/Front/skydivelogs-app/src/app/list-of-tunnel-flights/list-of-tunnel-flights.component.ts index 033ed82..e8ffebf 100644 --- a/Front/skydivelogs-app/src/app/list-of-tunnel-flights/list-of-tunnel-flights.component.ts +++ b/Front/skydivelogs-app/src/app/list-of-tunnel-flights/list-of-tunnel-flights.component.ts @@ -1,233 +1,253 @@ -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 { 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 { ServiceComm } from '../../services/service-comm.service'; +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"], + standalone: false, }) 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 = new MatTableDataSource(); - public displayedColumns: Array = [ - "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 = + new MatTableDataSource(); + public displayedColumns: Array = [ + "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 { - let results: Array = []; - 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 { - let tmpResults = new Map(); - - 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 = 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 { + let results: Array = []; + 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 { + let tmpResults = new Map(); + + 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 = this.dataSourceTable.data; + data = data.filter((d) => d.id !== item.id); + + this.dataSourceTable.data = data; + this.serviceTunnelFlight.deleteTunnelFlight(item); + } } diff --git a/Front/skydivelogs-app/src/app/login-user/login-user.component.html b/Front/skydivelogs-app/src/app/login-user/login-user.component.html index 4e92fc6..885a037 100644 --- a/Front/skydivelogs-app/src/app/login-user/login-user.component.html +++ b/Front/skydivelogs-app/src/app/login-user/login-user.component.html @@ -1,36 +1,50 @@ -
+

- {{ 'LoginUser_Username' | translate }} - - - {{ 'LoginUser_UsernameRequired' | translate }} + {{ "LoginUser_Username" | translate }} + + + {{ "LoginUser_UsernameRequired" | translate }} - + {{ 'LoginUser_UsernamePattern | translate }}

- {{ 'LoginUser_Password' | translate }} - - - {{ 'LoginUser_PasswordRequired' | translate }} + {{ "LoginUser_Password" | translate }} + + + {{ "LoginUser_PasswordRequired" | translate }} - - {{ 'LoginUser_PasswordPattern' | translate }} + + {{ "LoginUser_PasswordPattern" | translate }}

-
{{error}}
+
{{ error }}
diff --git a/Front/skydivelogs-app/src/app/login-user/login-user.component.ts b/Front/skydivelogs-app/src/app/login-user/login-user.component.ts index 171db28..6c9773e 100644 --- a/Front/skydivelogs-app/src/app/login-user/login-user.component.ts +++ b/Front/skydivelogs-app/src/app/login-user/login-user.component.ts @@ -1,32 +1,34 @@ -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 { FormBuilder, FormGroup, Validators } from "@angular/forms"; +import { MatInput } from "@angular/material/input"; -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"], + standalone: false, }) export class LoginUserComponent implements OnInit, AfterViewInit { loginForm: FormGroup; loading = false; submitted = false; returnUrl: string; - error = ''; - @ViewChild('username') userNameInput: MatInput; + error = ""; + @ViewChild("username") userNameInput: MatInput; - 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(["/"]); } } @@ -37,15 +39,14 @@ export class LoginUserComponent implements OnInit, AfterViewInit { 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() { @@ -57,17 +58,21 @@ export class LoginUserComponent implements OnInit, AfterViewInit { 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( + (data) => { + this.router.navigate([this.returnUrl]); + }, + (error) => { + this.error = error; + this.loading = false; + } + ); } } } diff --git a/Front/skydivelogs-app/src/services/request-cache.service.ts b/Front/skydivelogs-app/src/services/request-cache.service.ts index 589c22f..85b21bc 100644 --- a/Front/skydivelogs-app/src/services/request-cache.service.ts +++ b/Front/skydivelogs-app/src/services/request-cache.service.ts @@ -1,5 +1,5 @@ -import { Injectable } from '@angular/core'; -import { HttpRequest, HttpResponse } from '@angular/common/http'; +import { Injectable } from "@angular/core"; +import { HttpRequest, HttpResponse } from "@angular/common/http"; @Injectable() export class RequestCache { @@ -10,8 +10,8 @@ export class RequestCache { } get(req: HttpRequest): HttpResponse | undefined { - const splittedUrl = req.urlWithParams.split('/'); - const url = splittedUrl[splittedUrl.length - 1] + '_' + req.method; + const splittedUrl = req.urlWithParams.split("/"); + const url = splittedUrl[splittedUrl.length - 1] + "_" + req.method; this.clearExpiredEntries(); const cached = this.cache.get(url); @@ -24,8 +24,8 @@ export class RequestCache { } put(req: HttpRequest, response: HttpResponse): void { - const splittedUrl = req.urlWithParams.split('/'); - const url = splittedUrl[splittedUrl.length - 1] + '_' + req.method; + const splittedUrl = req.urlWithParams.split("/"); + const url = splittedUrl[splittedUrl.length - 1] + "_" + req.method; const entry = { url, response, lastRead: Date.now() }; this.cache.set(url, entry); @@ -36,7 +36,7 @@ export class RequestCache { const maxAge = 30000; const expired = Date.now() - maxAge; - this.cache.forEach(expiredEntry => { + this.cache.forEach((expiredEntry) => { if (expiredEntry.lastRead < expired) { this.cache.delete(expiredEntry.url); } diff --git a/Front/skydivelogs-app/src/tsconfig.app.json b/Front/skydivelogs-app/src/tsconfig.app.json deleted file mode 100644 index c1c97c7..0000000 --- a/Front/skydivelogs-app/src/tsconfig.app.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "extends": "../tsconfig.json", - "compilerOptions": { - "outDir": "../out-tsc/app", - "baseUrl": "./", - "types": [] - }, - "files": [ - "main.ts", - "polyfills.ts" - ] -} diff --git a/Front/skydivelogs-app/src/tsconfig.spec.json b/Front/skydivelogs-app/src/tsconfig.spec.json deleted file mode 100644 index c89454b..0000000 --- a/Front/skydivelogs-app/src/tsconfig.spec.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "extends": "../tsconfig.json", - "compilerOptions": { - "outDir": "../out-tsc/spec", - "baseUrl": "./", - "types": [ - "jasmine", - "node" - ] - }, - "files": [ - "test.ts", - "polyfills.ts" - ], - "include": [ - "**/*.spec.ts", - "**/*.d.ts" - ] -} diff --git a/Front/skydivelogs-app/tsconfig.app.json b/Front/skydivelogs-app/tsconfig.app.json new file mode 100644 index 0000000..3775b37 --- /dev/null +++ b/Front/skydivelogs-app/tsconfig.app.json @@ -0,0 +1,15 @@ +/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ +/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./out-tsc/app", + "types": [] + }, + "files": [ + "src/main.ts" + ], + "include": [ + "src/**/*.d.ts" + ] +} diff --git a/Front/skydivelogs-app/tsconfig.json b/Front/skydivelogs-app/tsconfig.json index 4a3997b..263b4d7 100644 --- a/Front/skydivelogs-app/tsconfig.json +++ b/Front/skydivelogs-app/tsconfig.json @@ -1,25 +1,29 @@ +/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ +/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ { "compileOnSave": false, - "angularCompilerOptions": { - "strictTemplates": true, - }, "compilerOptions": { - "importHelpers": true, "outDir": "./dist/out-tsc", + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "skipLibCheck": true, + "isolatedModules": true, "esModuleInterop": true, - "sourceMap": true, - "declaration": false, - "moduleResolution": "node", "experimentalDecorators": true, - "target": "es2015", - "typeRoots": [ - "node_modules/@types" - ], - "lib": [ - "es2017", - "dom" - ], - "module": "esnext", - "baseUrl": "./" + "moduleResolution": "bundler", + "importHelpers": true, + "target": "ES2022", + "module": "ES2022", + "strictPropertyInitialization": false, + "strictNullChecks": false + }, + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true } -} \ No newline at end of file +} diff --git a/Front/skydivelogs-app/tsconfig.spec.json b/Front/skydivelogs-app/tsconfig.spec.json new file mode 100644 index 0000000..5fb748d --- /dev/null +++ b/Front/skydivelogs-app/tsconfig.spec.json @@ -0,0 +1,15 @@ +/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ +/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./out-tsc/spec", + "types": [ + "jasmine" + ] + }, + "include": [ + "src/**/*.spec.ts", + "src/**/*.d.ts" + ] +}