Add a page to show the hours of tunnel

This commit is contained in:
Sébastien ANDRE
2023-06-22 14:28:46 +02:00
parent d651d365b7
commit ef15986db3
12 changed files with 271 additions and 73 deletions

View File

@@ -21,6 +21,8 @@
"@angular/router": "^16.1.1",
"@ngx-translate/core": "^14.0.0",
"@ngx-translate/http-loader": "^7.0.0",
"chart.js": "^4.3.0",
"ng2-charts": "^4.1.1",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"zone.js": "~0.13.1"
@@ -2957,6 +2959,11 @@
"integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
"dev": true
},
"node_modules/@kurkle/color": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz",
"integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw=="
},
"node_modules/@leichtgewicht/ip-codec": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz",
@@ -5098,6 +5105,17 @@
"integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==",
"dev": true
},
"node_modules/chart.js": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.3.0.tgz",
"integrity": "sha512-ynG0E79xGfMaV2xAHdbhwiPLczxnNNnasrmPEXriXsPJGjmhOBYzFVEsB65w2qMDz+CaBJJuJD0inE/ab/h36g==",
"dependencies": {
"@kurkle/color": "^0.3.0"
},
"engines": {
"pnpm": ">=7"
}
},
"node_modules/chokidar": {
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
@@ -8286,6 +8304,11 @@
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"dev": true
},
"node_modules/lodash-es": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
},
"node_modules/lodash.debounce": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
@@ -8954,6 +8977,22 @@
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
"dev": true
},
"node_modules/ng2-charts": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/ng2-charts/-/ng2-charts-4.1.1.tgz",
"integrity": "sha512-iHwXDbmX86lfeH8VRcsaW2tJATsuAZo4kvvC/Yk2l35zOHjevja1qBvO6BAibiDazi9r9aS6ZRJOqWPsz1pP2w==",
"dependencies": {
"lodash-es": "^4.17.15",
"tslib": "^2.3.0"
},
"peerDependencies": {
"@angular/cdk": ">=14.0.0",
"@angular/common": ">=14.0.0",
"@angular/core": ">=14.0.0",
"chart.js": "^3.4.0 || ^4.0.0",
"rxjs": "^6.5.3 || ^7.4.0"
}
},
"node_modules/nice-napi": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz",

View File

@@ -24,6 +24,8 @@
"@angular/router": "^16.1.1",
"@ngx-translate/core": "^14.0.0",
"@ngx-translate/http-loader": "^7.0.0",
"chart.js": "^4.3.0",
"ng2-charts": "^4.1.1",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"zone.js": "~0.13.1"
@@ -41,4 +43,4 @@
"karma-jasmine-html-reporter": "~2.0.0",
"typescript": "~4.9.4"
}
}
}

View File

@@ -31,6 +31,10 @@
<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-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>

View File

@@ -28,6 +28,7 @@ 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";
@@ -67,6 +68,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 { JwtAuthInterceptor } from "../interceptor/jwt-auth.interceptor";
import { ErrorInterceptor } from "../interceptor/error.interceptor";
@@ -118,6 +120,12 @@ const appRoutes: Routes = [
component: NewTunnelFlightComponent,
canActivate: [AuthGuardService]
},
{
path: "tunnelFlights",
component: ListOfTunnelFlightsComponent,
canActivate: [AuthGuardService]
},
{ path: "login", component: LoginComponent },
];
@@ -152,7 +160,8 @@ export function initConfig(configService: ConfigurationHelper) {
UserProfileComponent,
ListOfImagesComponent,
JumpInfosComponent,
NewTunnelFlightComponent
NewTunnelFlightComponent,
ListOfTunnelFlightsComponent
],
imports: [
RouterModule.forRoot(
@@ -191,7 +200,8 @@ export function initConfig(configService: ConfigurationHelper) {
MatRadioModule,
MatSidenavModule,
MatListModule,
MatToolbarModule
MatToolbarModule,
NgChartsModule
],
exports: [HttpClientModule],
providers: [

View File

@@ -1,76 +1,82 @@
<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>{{ 'List_Jump_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;"></th>
<td mat-cell *matCellDef="let element" style="text-align: left;">
<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;"></th>
<td mat-cell *matCellDef="let element" style="text-align: left;">
<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>
<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;">{{ '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="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>{{ '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="jumpType">
<th mat-header-cell *matHeaderCellDef style="min-width: 100px;">{{ 'List_Jump_Header_JumpType' | translate }}</th>
<td mat-cell *matCellDef="let element">
<span class="smallSpanWithBreakWord" [innerHTML]="element.jumpType.name"></span>
</td>
</ng-container>
<ng-container matColumnDef="jumpType">
<th mat-header-cell *matHeaderCellDef style="min-width: 100px;">{{ 'List_Jump_Header_JumpType' |
translate }}</th>
<td mat-cell *matCellDef="let element">
<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;">{{ '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="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>{{ '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="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>{{ 'List_Jump_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: 70px;"></th>
<td mat-cell *matCellDef="let element" style="text-align: left;">
<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: 70px;"></th>
<td mat-cell *matCellDef="let element" style="text-align: left;">
<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>
<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

@@ -0,0 +1,13 @@
<div class="content">
<mat-progress-bar [mode]="'indeterminate'" *ngIf="isLoading"></mat-progress-bar>
<div>
<canvas baseChart style="width: 50%;"
[data]="barChartData"
[options]="barChartOptions"
[plugins]="barChartPlugins"
[legend]="barChartLegend"
[type]="barChartType" >
</canvas>
</div>
</div>

View File

@@ -0,0 +1,21 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ListOfTunnelFlightsComponent } from './list-of-tunnel-flights.component';
describe('ListOfTunnelFlightsComponent', () => {
let component: ListOfTunnelFlightsComponent;
let fixture: ComponentFixture<ListOfTunnelFlightsComponent>;
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [ListOfTunnelFlightsComponent]
});
fixture = TestBed.createComponent(ListOfTunnelFlightsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,93 @@
import { Component, OnInit } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { ChartConfiguration, ChartData } from 'chart.js';
import { TunnelService } from '../../services/tunnel.service';
import { ServiceComm } from '../../services/service-comm.service';
import { TunnelFlightService } from "../../services/tunnel-flight.service";
@Component({
selector: 'app-list-of-tunnel-flights',
templateUrl: './list-of-tunnel-flights.component.html',
styleUrls: ['./list-of-tunnel-flights.component.css']
})
export class ListOfTunnelFlightsComponent implements OnInit {
public barChartLegend = true;
public barChartPlugins = [];
public barChartData: ChartData<'bar'>;
public barChartOptions: ChartConfiguration['options'];
public barChartType: string;
public isLoading: boolean = false;
constructor(private serviceComm: ServiceComm,
private serviceTunnel: TunnelService,
private serviceTunnelFlight: TunnelFlightService,
private translateService: TranslateService) { }
ngOnInit() {
this.serviceComm.forceTranslateTitle.subscribe((data) => {
if (data === true) {
this.updateTitle();
}
});
this.updateTitle();
this.isLoading = true;
this.getData();
}
private updateTitle() {
this.translateService.get("ListTunnelFlight_Title").subscribe(
data => { this.serviceComm.UpdatedComponentTitle(data); }
);
}
private getData(): void {
this.barChartType = "bar";
this.barChartData = {
labels: ['2006', '2007', '2008', '2009', '2010', '2011', '2012'],
datasets: [
{ data: [65, 59, 80, 81, 56, 55, 40], label: 'Series A' },
{ data: [28, 48, 40, 19, 86, 27, 90], label: 'Series B' }
]
};
this.barChartOptions = {
responsive: false,
plugins: {
legend: {
display: true
},
tooltip: {
callbacks: {
footer: this.footer,
}
}
},
interaction: {
intersect: false,
mode: 'nearest',
axis: 'x'
},
scales: {
x: {
stacked: true
},
y: {
stacked: true
}
}
};
this.isLoading = false;
}
private footer = (tooltipItems) => {
let sum = 0;
tooltipItems.forEach(function (tooltipItem) {
sum += tooltipItem.parsed.y;
});
return 'Sum: ' + sum;
};
}

View File

@@ -47,10 +47,10 @@ export class NewTunnelFlightComponent implements OnInit {
private pendingAddRequest: boolean;
constructor(private serviceComm: ServiceComm,
private serviceTunnel: TunnelService,
private serviceTunnelFlight: TunnelFlightService,
private translateService: TranslateService,
private statsService: StatsService) { }
private serviceTunnel: TunnelService,
private serviceTunnelFlight: TunnelFlightService,
private translateService: TranslateService,
private statsService: StatsService) { }
ngOnInit() {
this.serviceComm.forceTranslateTitle.subscribe((data) => {

View File

@@ -37,6 +37,7 @@
"ListGears_Title" : "List of gears",
"ListAircrafts_Title" : "List of aircrafts",
"NewTunnelFlight_Title" : "New tunnel flights",
"ListTunnelFlight_Title" : "List of hours of tunnel",
"App_Footer" : "Web software to log your skydive jumps - v",
"App_Nav_Summary" : "Summary",
@@ -48,6 +49,7 @@
"App_Nav_Gears" : "List of gears",
"App_Nav_Logout" : "Logout",
"App_Nav_NewTunnelFlight" : "Add tunnel time",
"App_Nav_TunnelFlights": "The tunnel flights",
"List_Aircrafts_Add" : "Add a aircraft",
"List_Aircrafts_Header_Id" : "ID",
@@ -116,5 +118,8 @@
"NewTunnelFlight_ChooseTunnel": "Choose the tunnel",
"NewTunnelFlight_Minutes": "Minutes of the flight",
"NewTunnelFlight_Comments": "Comments",
"NewTunnelFlight_Submit": "Submit"
"NewTunnelFlight_Submit": "Submit",
"NewTunnelFlight_Comments_Lbl": "Comments",
"NewTunnelFlight_Minutes_Lbl": "Time of flight (minutes)",
"NewTunnelFlight_Date_Lbl": "Date of flight"
}

View File

@@ -37,6 +37,7 @@
"ListGears_Title" : "Liste des pièges",
"ListAircrafts_Title" : "Liste des avions",
"NewTunnelFlight_Title" : "Nouveaux créneaux de soufflerie",
"ListTunnelFlight_Title" : "Heures de tunnel",
"App_Footer" : "Application pour enregistrer ses sauts de parachutisme - v",
"App_Nav_Summary" : "Récapitulatif",
@@ -48,6 +49,7 @@
"App_Nav_Gears" : "Pièges",
"App_Nav_Logout" : "Se déconnecter",
"App_Nav_NewTunnelFlight" : "Ajouter du temps en tunnel",
"App_Nav_TunnelFlights": "Les vols en soufflerie",
"List_Aircrafts_Add" : "Ajouter un avion",
"List_Aircrafts_Header_Id" : "ID",
@@ -116,5 +118,8 @@
"NewTunnelFlight_ChooseTunnel": "Choisir le tunnel",
"NewTunnelFlight_Minutes": "Temps de vol(minutes)",
"NewTunnelFlight_Comments": "Commentaires",
"NewTunnelFlight_Submit": "Ajouter"
"NewTunnelFlight_Submit": "Ajouter",
"NewTunnelFlight_Comments_Lbl": "Commentaires",
"NewTunnelFlight_Minutes_Lbl": "Temps de vol(minutes)",
"NewTunnelFlight_Date_Lbl": "Date des vols"
}