chore(deps): upgrade Angular 18 → 19

- ng update @angular/core@19 @angular/cli@19 @angular/material@19 @angular-eslint/schematics@19
- Migration: remove standalone:true (now default in v19) from 60 components
- Migration: zone.js 0.14 → 0.15
- Fix pre-existing build budget error: raise initial bundle limit to 5mb (app ships large static JSON assets)
This commit is contained in:
2026-04-26 06:25:02 +02:00
parent b8eb8a9393
commit 5400294d45
65 changed files with 5875 additions and 4710 deletions
@@ -2,13 +2,10 @@ import { Component, Input, AfterContentChecked } from '@angular/core';
import { NgIf, NgFor } from '@angular/common';
@Component({
standalone: true,
imports: [
NgIf, NgFor
],
imports: [NgIf, NgFor],
selector: 'app-history-table',
templateUrl: './history-table.component.html',
styleUrls: []
styleUrls: [],
})
export class HistoryTableComponent implements AfterContentChecked {
public seriesColTotal: number[] = [];
@@ -38,7 +35,7 @@ export class HistoryTableComponent implements AfterContentChecked {
this.seriesColTotal = [...values];
this.rows.forEach((row: number[], rowIndex: number) => {
row.forEach((value: number, index: number) => {
this.seriesColTotal[index] = (this.seriesColTotal[index] + this.rows[rowIndex][index]);
this.seriesColTotal[index] = this.seriesColTotal[index] + this.rows[rowIndex][index];
});
this.grandTotal = this.seriesColTotal.reduce((partialSum, a) => partialSum + a, 0);
});
@@ -13,16 +13,15 @@ import { JumpPreviewComponent } from './jump-preview.component';
@Component({
selector: 'app-jump-list',
templateUrl: './jump-list.component.html',
standalone: true,
imports: [
NgClass,
DatePipe,
MatProgressSpinnerModule,
MatButtonModule,
RouterLink,
MatIconModule,
JumpPreviewComponent
]
NgClass,
DatePipe,
MatProgressSpinnerModule,
MatButtonModule,
RouterLink,
MatIconModule,
JumpPreviewComponent,
],
})
export class JumpListComponent implements OnInit, OnDestroy {
private _jumps: Subscription = new Subscription();
@@ -35,9 +34,7 @@ export class JumpListComponent implements OnInit, OnDestroy {
pages: Array<number> = [1];
lastUpdate: Date = new Date();
constructor(
private jumpsService: JumpsService
) { }
constructor(private jumpsService: JumpsService) {}
@Input() limit = 0;
@Input() refresh = 30000;
@@ -70,7 +67,7 @@ export class JumpListComponent implements OnInit, OnDestroy {
runQuery() {
if (this.limit) {
this.query.filters.limit = this.limit;
this.query.filters.offset = (this.limit * (this.currentPage - 1));
this.query.filters.offset = this.limit * (this.currentPage - 1);
}
const jumps$: Observable<JumpList> = this.jumpsService.query(this.query);
@@ -85,7 +82,7 @@ export class JumpListComponent implements OnInit, OnDestroy {
},
error: (err) => {
this.errors = err;
}
},
});
}
@@ -6,10 +6,9 @@ import { Jump } from '@models';
@Component({
selector: 'app-jump-meta',
standalone: true,
imports: [ RouterLink, DatePipe ],
imports: [RouterLink, DatePipe],
styleUrl: './jump-meta.component.scss',
templateUrl: './jump-meta.component.html'
templateUrl: './jump-meta.component.html',
})
export class JumpMetaComponent {
@Input() jump!: Jump;
@@ -12,14 +12,9 @@ import { JumpsService } from '@services';
@Component({
selector: 'app-jump-preview',
standalone: true,
imports: [
DecimalPipe, RouterLink,
MatDividerModule, MatButtonModule, MatCardModule,
JumpMetaComponent
],
imports: [DecimalPipe, RouterLink, MatDividerModule, MatButtonModule, MatCardModule, JumpMetaComponent],
styleUrl: './jump-preview.component.scss',
templateUrl: './jump-preview.component.html'
templateUrl: './jump-preview.component.html',
})
export class JumpPreviewComponent implements OnDestroy {
@Input() jump!: Jump;
@@ -30,8 +25,8 @@ export class JumpPreviewComponent implements OnDestroy {
constructor(
private router: Router,
private jumpsService: JumpsService
) { }
private jumpsService: JumpsService,
) {}
ngOnDestroy() {
this._jump.unsubscribe();
@@ -43,7 +38,7 @@ export class JumpPreviewComponent implements OnDestroy {
this._jump = jump$.subscribe({
next: () => {
this.router.navigateByUrl('/');
}
},
});
}
}
@@ -1,4 +1,13 @@
import { AfterContentChecked, Component, EventEmitter, Input, Output, OnChanges, OnDestroy, ViewChild } from '@angular/core';
import {
AfterContentChecked,
Component,
EventEmitter,
Input,
Output,
OnChanges,
OnDestroy,
ViewChild,
} from '@angular/core';
import { CommonModule, DecimalPipe, DatePipe } from '@angular/common';
import { UntypedFormBuilder, UntypedFormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms';
import { Router, RouterLink } from '@angular/router';
@@ -15,29 +24,68 @@ import { MatProgressBarModule } from '@angular/material/progress-bar';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatSort, MatSortModule } from '@angular/material/sort';
import { MatTableDataSource, MatTableModule } from '@angular/material/table';
import { MatSnackBar, MatSnackBarHorizontalPosition, MatSnackBarModule, MatSnackBarVerticalPosition } from '@angular/material/snack-bar';
import {
MatSnackBar,
MatSnackBarHorizontalPosition,
MatSnackBarModule,
MatSnackBarVerticalPosition,
} from '@angular/material/snack-bar';
import { Observable, Subscription } from 'rxjs';
import { take } from 'rxjs/operators';
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
import { FrenchPaginator } from '@components/shared/french-paginator.component';
import { ColumnDefinition, Errors, Jump, JumpByDay, JumpFile, JumpFileType, JumpList, JumpListConfig, jumpColumns } from '@models';
import {
ColumnDefinition,
Errors,
Jump,
JumpByDay,
JumpFile,
JumpFileType,
JumpList,
JumpListConfig,
jumpColumns,
} from '@models';
import { JumpsService, JumpTableService } from '@services';
import { JumpAddDialogComponent, JumpDeleteDialogComponent, JumpEditDialogComponent, JumpViewDialogComponent } from '@components/logbook/dialogs'
import {
JumpAddDialogComponent,
JumpDeleteDialogComponent,
JumpEditDialogComponent,
JumpViewDialogComponent,
} from '@components/logbook/dialogs';
@Component({
standalone: true,
imports: [
CommonModule, DecimalPipe, DatePipe, RouterLink, FormsModule, ReactiveFormsModule, NgxSkeletonLoaderModule,
MatButtonModule, MatCardModule, MatDialogModule,
MatFormFieldModule, MatIconModule, MatInputModule, MatNativeDateModule, MatMenuModule,
MatPaginatorModule, MatProgressBarModule, MatProgressSpinnerModule, MatSnackBarModule, MatSortModule, MatTableModule,
JumpAddDialogComponent, JumpDeleteDialogComponent, JumpEditDialogComponent, JumpViewDialogComponent
CommonModule,
DecimalPipe,
DatePipe,
RouterLink,
FormsModule,
ReactiveFormsModule,
NgxSkeletonLoaderModule,
MatButtonModule,
MatCardModule,
MatDialogModule,
MatFormFieldModule,
MatIconModule,
MatInputModule,
MatNativeDateModule,
MatMenuModule,
MatPaginatorModule,
MatProgressBarModule,
MatProgressSpinnerModule,
MatSnackBarModule,
MatSortModule,
MatTableModule,
JumpAddDialogComponent,
JumpDeleteDialogComponent,
JumpEditDialogComponent,
JumpViewDialogComponent,
],
selector: 'app-jump-table',
styleUrl: './jump-table.component.scss',
templateUrl: './jump-table.component.html',
providers: [{provide: MatPaginatorIntl, useClass: FrenchPaginator}]
providers: [{ provide: MatPaginatorIntl, useClass: FrenchPaginator }],
})
export class JumpTableComponent implements OnChanges, OnDestroy, AfterContentChecked {
private _jumps: Subscription = new Subscription();
@@ -57,10 +105,18 @@ export class JumpTableComponent implements OnChanges, OnDestroy, AfterContentChe
'zone', 'files', 'actions'
];*/
public displayedColumns: string[] = [
'numero', 'date', 'lieu', 'aeronef',
'hauteur', 'voile', 'categorie',
'participants', 'zone', 'accessoires',
'files', 'actions'
'numero',
'date',
'lieu',
'aeronef',
'hauteur',
'voile',
'categorie',
'participants',
'zone',
'accessoires',
'files',
'actions',
];
//public displayedColumns: string[] = jumpColumns.map((col) => col.key)
public columnsSchema: Array<ColumnDefinition> = jumpColumns;
@@ -81,12 +137,12 @@ export class JumpTableComponent implements OnChanges, OnDestroy, AfterContentChe
private _jumpTableService: JumpTableService,
private dialog: MatDialog,
private fb: UntypedFormBuilder,
private snackBar: MatSnackBar
private snackBar: MatSnackBar,
) {
this._resetErrors();
this.searchForm = this.fb.group({
//numero: ['', Validators.required]
numero: ''
numero: '',
});
}
@@ -116,7 +172,7 @@ export class JumpTableComponent implements OnChanges, OnDestroy, AfterContentChe
ngOnDestroy() {
this._jumps.unsubscribe();
this._subscriptions.forEach(subscription => {
this._subscriptions.forEach((subscription) => {
subscription.unsubscribe();
});
}
@@ -139,7 +195,7 @@ export class JumpTableComponent implements OnChanges, OnDestroy, AfterContentChe
if (this.limit) {
this._query.filters.limit = this.limit;
this._query.filters.offset = (this.limit * (this._currentPage - 1));
this._query.filters.offset = this.limit * (this._currentPage - 1);
} else {
this._query.filters.limit = 0;
this._query.filters.offset = 0;
@@ -172,7 +228,7 @@ export class JumpTableComponent implements OnChanges, OnDestroy, AfterContentChe
},
error: (err) => {
this.errors = err;
}
},
});
}
@@ -185,97 +241,114 @@ export class JumpTableComponent implements OnChanges, OnDestroy, AfterContentChe
//config.filters.dateRangeEnd = `${year}-12-31 23:59:59`;
config.filters.dateRangeEnd = '2024-12-31 23:59:59';
//console.log(config.filters);
const subscription: Subscription = this._jumpsService.getAllByDay(config)
.pipe(take(1))
.subscribe({
next: (days) => {
this._resetErrors();
//console.log('getAllByDay : ', days);
days.forEach(day => {
this.loadSkydiverIdJumps(day, config);
});
},
error: (err) => {
this.errors = err;
}
});
const subscription: Subscription = this._jumpsService
.getAllByDay(config)
.pipe(take(1))
.subscribe({
next: (days) => {
this._resetErrors();
//console.log('getAllByDay : ', days);
days.forEach((day) => {
this.loadSkydiverIdJumps(day, config);
});
},
error: (err) => {
this.errors = err;
},
});
this._subscriptions.push(subscription);
}
loadSkydiverIdJumps(day: JumpByDay, config: JumpListConfig) {
config.filters.dateRangeStart = `${day.jumps[0].date!.substring(0, 10)} 00:00:00`;
config.filters.dateRangeEnd = `${day.jumps[0].date!.substring(0, 10)} 23:59:59`;
const subscription: Subscription = this._jumpsService.getAllFromSkydiverIdApi(config)
.pipe(take(1))
.subscribe({
next: (data) => {
if (data.items.length) {
const diff = day.count - data.items.length;
if (diff < 0) {
console.error(`${diff} ${diff > 1 ? 'données' : 'donnée'} altimètre de trop le ${day.jumps[0].date!.substring(0, 10)}`);
} else {
if (diff > 0) {
console.error(`${diff}/${day.count} ${diff > 1 ? 'données altimètre manquantes' : 'donnée altimètre manquante'} le ${day.jumps[0].date!.substring(0, 10)}`, day.jumps, data.items);
}
console.log(`${day.count} ${day.count > 1 ? 'sauts' : 'saut'} le ${data.items[0].date!.substring(0, 10)}`, day.jumps.sort((a, b) => b.numero! - a.numero!), data.items);
data.items.map((jump, index) => {
const file: JumpFile = {
name: jump.name,
path: `/Volumes/Storage/Skydive/X2_Logs/${day.jumps[index].date!.substring(0, 4)}/`,
type: JumpFileType.CSV
} as JumpFile;
this._subscriptions.push(
this._jumpsService.saveX2Data(day.jumps[index].slug!, file, jump)
.pipe(take(1))
.subscribe({
next: (jump) => {
this._resetErrors();
console.log(`Le fichier ${file.name} a été ajouté au saut n°${jump.numero} !`, 'OK');
},
error: (err) => {
this.errors = err;
}
})
const subscription: Subscription = this._jumpsService
.getAllFromSkydiverIdApi(config)
.pipe(take(1))
.subscribe({
next: (data) => {
if (data.items.length) {
const diff = day.count - data.items.length;
if (diff < 0) {
console.error(
`${diff} ${diff > 1 ? 'données' : 'donnée'} altimètre de trop le ${day.jumps[0].date!.substring(0, 10)}`,
);
return jump;
});
} else {
if (diff > 0) {
console.error(
`${diff}/${day.count} ${diff > 1 ? 'données altimètre manquantes' : 'donnée altimètre manquante'} le ${day.jumps[0].date!.substring(0, 10)}`,
day.jumps,
data.items,
);
}
console.log(
`${day.count} ${day.count > 1 ? 'sauts' : 'saut'} le ${data.items[0].date!.substring(0, 10)}`,
day.jumps.sort((a, b) => b.numero! - a.numero!),
data.items,
);
data.items.map((jump, index) => {
const file: JumpFile = {
name: jump.name,
path: `/Volumes/Storage/Skydive/X2_Logs/${day.jumps[index].date!.substring(0, 4)}/`,
type: JumpFileType.CSV,
} as JumpFile;
this._subscriptions.push(
this._jumpsService
.saveX2Data(day.jumps[index].slug!, file, jump)
.pipe(take(1))
.subscribe({
next: (jump) => {
this._resetErrors();
console.log(
`Le fichier ${file.name} a été ajouté au saut n°${jump.numero} !`,
'OK',
);
},
error: (err) => {
this.errors = err;
},
}),
);
return jump;
});
}
//console.log(`${day.count} ${day.count > 1 ? 'sauts' : 'saut'} le ${day.jumps[0].date!.substring(0, 10)}`);
//console.log(`${data.items.length} ${data.items.length > 1 ? 'sauts' : 'saut'} le ${data.items[0].date!.substring(0, 10)}`, data);
} else {
console.info('Aucune données altimètre pour le ', day.jumps[0].date!.substring(0, 10));
}
//console.log(`${day.count} ${day.count > 1 ? 'sauts' : 'saut'} le ${day.jumps[0].date!.substring(0, 10)}`);
//console.log(`${data.items.length} ${data.items.length > 1 ? 'sauts' : 'saut'} le ${data.items[0].date!.substring(0, 10)}`, data);
} else {
console.info('Aucune données altimètre pour le ', day.jumps[0].date!.substring(0, 10));
}
},
error: (err) => {
console.error('getAllFromSkydiverIdApi error');
this.errors = err;
}
});
},
error: (err) => {
console.error('getAllFromSkydiverIdApi error');
this.errors = err;
},
});
this._subscriptions.push(subscription);
}
updateJumps(jumps: Array<Jump>) {
jumps.map(jump => {
jumps.map((jump) => {
jump.files = [];
if (jump.dossier !== "" && jump.video !== "") {
if (jump.dossier !== '' && jump.video !== '') {
const file: JumpFile = {
name: jump.video!,
path: jump.dossier!,
type: JumpFileType.VIDEO
type: JumpFileType.VIDEO,
} as JumpFile;
this._subscriptions.push(
this._jumpsService.saveFile(jump.slug, file)
.pipe(take(1))
.subscribe({
next: (file) => {
this._resetErrors();
jump.files.push(file);
//console.log(`Le fichier ${file.name} a été ajouté au saut n°${jump.numero} !`, 'OK');
},
error: (err) => {
this.errors = err;
}
})
this._jumpsService
.saveFile(jump.slug, file)
.pipe(take(1))
.subscribe({
next: (file) => {
this._resetErrors();
jump.files.push(file);
//console.log(`Le fichier ${file.name} a été ajouté au saut n°${jump.numero} !`, 'OK');
},
error: (err) => {
this.errors = err;
},
}),
);
}
return jump;
@@ -283,26 +356,30 @@ export class JumpTableComponent implements OnChanges, OnDestroy, AfterContentChe
}
getKmlJumpFiles(jumps: Array<Jump>) {
jumps.map(jump => {
jumps.map((jump) => {
if (jump.x2data !== null) {
//console.log(`Le fichier ${jump.x2data.name} existe pour le saut n°${jump.numero} !`, `https://skydiver.id/api/jump/${jump.x2data.id}/kml`);
const file: JumpFile = {
name: jump.x2data.name.substring(0, -4) + '.kml',
path: `/X2_Kmls/${jump.date.substring(0, 4)}/`,
type: JumpFileType.KML
type: JumpFileType.KML,
} as JumpFile;
this._subscriptions.push(
this._jumpsService.saveKml(jump.slug, file)
.pipe(take(1))
.subscribe({
next: (file) => {
this._resetErrors();
console.log(`Le fichier ${file.path}${file.name} a été ajouté au saut n°${jump.numero} !`, 'OK');
},
error: (err) => {
this.errors = err;
}
})
this._jumpsService
.saveKml(jump.slug, file)
.pipe(take(1))
.subscribe({
next: (file) => {
this._resetErrors();
console.log(
`Le fichier ${file.path}${file.name} a été ajouté au saut n°${jump.numero} !`,
'OK',
);
},
error: (err) => {
this.errors = err;
},
}),
);
}
return jump;
@@ -312,50 +389,53 @@ export class JumpTableComponent implements OnChanges, OnDestroy, AfterContentChe
deleteJump(slug: string) {
this.isDeleting = true;
this._subscriptions.push(
this._jumpsService.destroy(slug)
.pipe(take(1))
.subscribe(() => {
this.router.navigateByUrl('/');
})
this._jumpsService
.destroy(slug)
.pipe(take(1))
.subscribe(() => {
this.router.navigateByUrl('/');
}),
);
}
openDeleteDialog(jump: Jump): void {
const dialogRef = this.dialog.open(JumpDeleteDialogComponent, {
width: '60vw',
data: jump
data: jump,
});
this._subscriptions.push(
dialogRef.afterClosed()
.pipe(take(1))
.subscribe(result => {
if (result != undefined) {
this._onDelete(result.slug);
}
})
dialogRef
.afterClosed()
.pipe(take(1))
.subscribe((result) => {
if (result != undefined) {
this._onDelete(result.slug);
}
}),
);
}
openEditDialog(jump: Jump): void {
const dialogRef = this.dialog.open(JumpEditDialogComponent, {
width: '70vw',
data: jump
data: jump,
});
this._subscriptions.push(
dialogRef.afterClosed()
.pipe(take(1))
.subscribe(result => {
if (result != undefined) {
this._onEdit(result);
}
})
dialogRef
.afterClosed()
.pipe(take(1))
.subscribe((result) => {
if (result != undefined) {
this._onEdit(result);
}
}),
);
}
openViewDialog(jump: Jump): void {
const dialogRef = this.dialog.open(JumpViewDialogComponent, {
width: '60vw',
data: jump
data: jump,
});
dialogRef.afterClosed();
}
@@ -379,20 +459,21 @@ export class JumpTableComponent implements OnChanges, OnDestroy, AfterContentChe
try {
this.isDeleting = true;
this._subscriptions.push(
this._jumpsService.destroy(slug)
.pipe(take(1))
.subscribe({
next: () => {
this._resetErrors();
this._openSnackBar(`Le saut a été supprimé !`, 'OK');
this.runQuery();
this.tableRefresh = !this.tableRefresh;
this._jumpTableService.updateTableRefresh(this.tableRefresh);
},
error: (err) => {
this.errors = err;
}
})
this._jumpsService
.destroy(slug)
.pipe(take(1))
.subscribe({
next: () => {
this._resetErrors();
this._openSnackBar(`Le saut a été supprimé !`, 'OK');
this.runQuery();
this.tableRefresh = !this.tableRefresh;
this._jumpTableService.updateTableRefresh(this.tableRefresh);
},
error: (err) => {
this.errors = err;
},
}),
);
} catch (error) {
console.error(error);
@@ -403,21 +484,22 @@ export class JumpTableComponent implements OnChanges, OnDestroy, AfterContentChe
try {
this.isSubmitting = true;
this._subscriptions.push(
this._jumpsService.update(jump)
.pipe(take(1))
.subscribe({
next: (jump) => {
this._resetErrors();
this._openSnackBar(`Le saut n°${jump.numero} a été mis à jour !`, 'OK');
this.runQuery();
this.tableRefresh = !this.tableRefresh;
this._jumpTableService.updateTableRefresh(this.tableRefresh);
},
error: (err) => {
this.errors = err;
this.isSubmitting = false;
}
})
this._jumpsService
.update(jump)
.pipe(take(1))
.subscribe({
next: (jump) => {
this._resetErrors();
this._openSnackBar(`Le saut n°${jump.numero} a été mis à jour !`, 'OK');
this.runQuery();
this.tableRefresh = !this.tableRefresh;
this._jumpTableService.updateTableRefresh(this.tableRefresh);
},
error: (err) => {
this.errors = err;
this.isSubmitting = false;
},
}),
);
} catch (error) {
console.error(error);
@@ -468,5 +550,4 @@ export class JumpTableComponent implements OnChanges, OnDestroy, AfterContentChe
}))
}
*/
}