Import des sources angular à partir de 'headup_app'
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
export * from './jump-add.dialog';
|
||||
export * from './jump-delete.dialog';
|
||||
export * from './jump-edit.dialog';
|
||||
export * from './jump-view.dialog';
|
||||
@@ -0,0 +1,301 @@
|
||||
<h2 mat-dialog-title class="bg-warn border-bottom border-light-subtle d-flex mb-3 pt-3 pb-2">
|
||||
Ajouter un saut<span class="flex-spacer"></span>
|
||||
<mat-icon fontIcon="close" class="pointer" (click)="closeDialog()"></mat-icon>
|
||||
</h2>
|
||||
<mat-dialog-content>
|
||||
<div class="container">
|
||||
<!--
|
||||
<div class="row">
|
||||
<div class="col-xs-12 border-bottom border-dark pb-2 mb-2">
|
||||
<mat-form-field appearance="outline" class="mt-2 me-2" color="warn">
|
||||
<mat-label>Numéro ou plage de saut à ajouter</mat-label>
|
||||
<input matInput [(ngModel)]="params.numeros" required />
|
||||
<mat-hint>645 / +5 / 650-660</mat-hint>
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="outline" class="mt-2 me-3" color="warn">
|
||||
<mat-label>Choisissez une date</mat-label>
|
||||
<input matInput [matDatepicker]="datepicker" [(ngModel)]="params.date" (dateChange)="onDateChange()" required />
|
||||
<mat-hint>MM/DD/YYYY</mat-hint>
|
||||
<mat-datepicker-toggle matIconSuffix [for]="datepicker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #datepicker>
|
||||
<mat-datepicker-actions>
|
||||
<button mat-button matDatepickerCancel>Annuler</button>
|
||||
<button mat-raised-button color="warn" matDatepickerApply>Appliquer</button>
|
||||
</mat-datepicker-actions>
|
||||
</mat-datepicker>
|
||||
</mat-form-field>
|
||||
<mat-checkbox (change)="setLastJump($event.checked)" [checked]="useLast.all">
|
||||
<mat-icon fontIcon="history" class="align-middle me-2"></mat-icon> <span class="d-inline-block align-middle">Utiliser les valeurs du dernier saut</span>
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
-->
|
||||
<form class="row" [formGroup]="jumpForm" (ngSubmit)="submitForm()">
|
||||
<div class="col-xs-12 border-bottom border-dark pb-2 mb-2">
|
||||
<mat-form-field appearance="outline" class="mt-2 me-2" color="warn">
|
||||
<mat-label>Numéro ou plage de saut à ajouter</mat-label>
|
||||
<input matInput formControlName="numero" required />
|
||||
<mat-hint>645 / +5 / 650-660</mat-hint>
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="outline" class="mt-2 me-3" color="warn">
|
||||
<mat-label>Choisissez une date</mat-label>
|
||||
<input matInput [matDatepicker]="datepicker" formControlName="date" (dateChange)="onDateChange()" required cdkFocusInitial />
|
||||
<mat-hint>MM/DD/YYYY</mat-hint>
|
||||
<mat-datepicker-toggle matIconSuffix [for]="datepicker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #datepicker>
|
||||
<mat-datepicker-actions>
|
||||
<button mat-button matDatepickerCancel>Annuler</button>
|
||||
<button mat-raised-button color="warn" matDatepickerApply>Appliquer</button>
|
||||
</mat-datepicker-actions>
|
||||
</mat-datepicker>
|
||||
</mat-form-field>
|
||||
<mat-checkbox (change)="setLastJump($event.checked)" [checked]="useLast.all">
|
||||
<mat-icon fontIcon="history" class="align-middle me-2"></mat-icon> <span class="d-inline-block align-middle">Utiliser les valeurs du dernier saut</span>
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
<div class="col-lg-6 col-md-12">
|
||||
<h4 class="border-bottom border-secondary mb-3 lh-lg">
|
||||
Dropzone
|
||||
<mat-checkbox class="float-end" (change)="setLastDropzone($event.checked)" [checked]="useLast.dropzone">
|
||||
<mat-icon fontIcon="history" class="align-middle"></mat-icon>
|
||||
</mat-checkbox>
|
||||
</h4>
|
||||
<mat-form-field appearance="fill" class="me-2" color="warn">
|
||||
<mat-label>Lieu</mat-label>
|
||||
<input matInput formControlName="lieu" [matAutocomplete]="autoDropzone">
|
||||
<mat-autocomplete #autoDropzone="matAutocomplete" [displayWith]="displayLieuFn">
|
||||
@for (option of filteredOptions.dropzones | async; track option) {
|
||||
<mat-option [value]="option" (onSelectionChange)="onSelLieu(option)">{{option.lieu}} - {{option.oaci}}</mat-option>
|
||||
}
|
||||
</mat-autocomplete>
|
||||
@if (this.jumpForm.controls['lieu'].status === 'INVALID') {
|
||||
<mat-error>{{getErrorMessage('lieu')}}</mat-error>
|
||||
}
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="fill" class="me-2" color="warn">
|
||||
<mat-label>Oaci</mat-label>
|
||||
<input matInput formControlName="oaci" [matAutocomplete]="autoOaci">
|
||||
<mat-autocomplete #autoOaci="matAutocomplete" [displayWith]="displayOaciFn">
|
||||
@for (option of filteredOptions.oaci | async; track option) {
|
||||
<mat-option [value]="option" (onSelectionChange)="onSelOaci(option)">{{option.lieu}} - {{option.oaci}}</mat-option>
|
||||
}
|
||||
</mat-autocomplete>
|
||||
@if (this.jumpForm.controls['oaci'].status === 'INVALID') {
|
||||
<mat-error>{{getErrorMessage('oaci')}}</mat-error>
|
||||
}
|
||||
</mat-form-field>
|
||||
<h4 class="border-bottom border-secondary mb-3 lh-lg">
|
||||
Aéronef
|
||||
<mat-checkbox class="float-end" (change)="setLastAeronef($event.checked)" [checked]="useLast.aeronef">
|
||||
<mat-icon fontIcon="history" class="align-middle"></mat-icon>
|
||||
</mat-checkbox>
|
||||
</h4>
|
||||
<mat-form-field appearance="fill" class="me-2" color="warn">
|
||||
<mat-label>Aéronef</mat-label>
|
||||
<input matInput formControlName="aeronef" [matAutocomplete]="autoAeronef">
|
||||
<mat-autocomplete #autoAeronef="matAutocomplete" [displayWith]="displayAeronefFn">
|
||||
@for (option of filteredOptions.aeronefs | async; track option) {
|
||||
<mat-option [value]="option" (onSelectionChange)="onSelAeronef(option)">{{option.aeronef}} - {{option.imat}}</mat-option>
|
||||
}
|
||||
</mat-autocomplete>
|
||||
@if (this.jumpForm.controls['aeronef'].status === 'INVALID') {
|
||||
<mat-error>{{getErrorMessage('aeronef')}}</mat-error>
|
||||
}
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="fill" class="me-2" color="warn">
|
||||
<mat-label>Imatriculation</mat-label>
|
||||
<input matInput formControlName="imat" [matAutocomplete]="autoImat">
|
||||
<mat-autocomplete #autoImat="matAutocomplete" [displayWith]="displayImatFn">
|
||||
@for (option of filteredOptions.imats | async; track option) {
|
||||
<mat-option [value]="option" (onSelectionChange)="onSelImat(option)">{{option.aeronef}} - {{option.imat}}</mat-option>
|
||||
}
|
||||
</mat-autocomplete>
|
||||
@if (this.jumpForm.controls['imat'].status === 'INVALID') {
|
||||
<mat-error>{{getErrorMessage('imat')}}</mat-error>
|
||||
}
|
||||
</mat-form-field>
|
||||
<h4 class="border-bottom border-secondary mb-3 lh-lg">
|
||||
Voile
|
||||
<mat-checkbox class="float-end" (change)="setLastVoile($event.checked)" [checked]="useLast.voile">
|
||||
<mat-icon fontIcon="history" class="align-middle"></mat-icon>
|
||||
</mat-checkbox>
|
||||
</h4>
|
||||
<mat-form-field appearance="fill" class="me-2" color="warn">
|
||||
<mat-label>Modèle</mat-label>
|
||||
<input matInput formControlName="voile" [matAutocomplete]="autoCanopy">
|
||||
<mat-autocomplete #autoCanopy="matAutocomplete" [displayWith]="displayVoileFn">
|
||||
@for (option of filteredOptions.canopies | async; track option) {
|
||||
<mat-option [value]="option" (onSelectionChange)="onSelVoile(option)">{{option.voile}} - {{option.taille}}</mat-option>
|
||||
}
|
||||
</mat-autocomplete>
|
||||
@if (this.jumpForm.controls['voile'].status === 'INVALID') {
|
||||
<mat-error>{{getErrorMessage('voile')}}</mat-error>
|
||||
}
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="fill" class="me-2" color="warn">
|
||||
<mat-label>Taille en ft<sup>2</sup></mat-label>
|
||||
<input matInput formControlName="taille" [matAutocomplete]="autoTaille">
|
||||
<mat-autocomplete #autoTaille="matAutocomplete" [displayWith]="displayTailleFn">
|
||||
@for (option of filteredOptions.tailles | async; track option) {
|
||||
<mat-option [value]="option" (onSelectionChange)="onSelTaille(option)">{{option.voile}} - {{option.taille}}</mat-option>
|
||||
}
|
||||
</mat-autocomplete>
|
||||
<span matTextSuffix>ft<sup>2</sup></span>
|
||||
@if (this.jumpForm.controls['taille'].status === 'INVALID') {
|
||||
<mat-error>{{getErrorMessage('taille')}}</mat-error>
|
||||
}
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="col-lg-6 col-md-12">
|
||||
<h4 class="border-bottom border-secondary mb-3 lh-lg">
|
||||
Altitude
|
||||
<mat-checkbox class="float-end" (change)="setLastAltitude($event.checked)" [checked]="useLast.altitude">
|
||||
<mat-icon fontIcon="history" class="align-middle"></mat-icon>
|
||||
</mat-checkbox>
|
||||
</h4>
|
||||
<mat-form-field appearance="fill" class="me-2" color="warn">
|
||||
<mat-label>Hauteur de largage</mat-label>
|
||||
<input matInput formControlName="hauteur" type="number" min="0" step="1">
|
||||
@if (this.jumpForm.controls['hauteur'].status === 'INVALID') {
|
||||
<mat-error>{{getErrorMessage('hauteur')}}</mat-error>
|
||||
}
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="fill" class="me-2" color="warn">
|
||||
<mat-label>Hauteur de déploiement</mat-label>
|
||||
<input matInput formControlName="deploiement" type="number" min="0" step="1">
|
||||
@if (this.jumpForm.controls['deploiement'].status === 'INVALID') {
|
||||
<mat-error>{{getErrorMessage('deploiement')}}</mat-error>
|
||||
}
|
||||
</mat-form-field>
|
||||
<h4 class="border-bottom border-secondary mb-3 lh-lg">
|
||||
Discipline
|
||||
<mat-checkbox class="float-end" (change)="setLastDiscipline($event.checked)" [checked]="useLast.discipline">
|
||||
<mat-icon fontIcon="history" class="align-middle"></mat-icon>
|
||||
</mat-checkbox>
|
||||
</h4>
|
||||
<mat-form-field appearance="fill" class="me-2" color="warn">
|
||||
<mat-label>Categorie</mat-label>
|
||||
<input matInput formControlName="categorie" [matAutocomplete]="autoCategorie">
|
||||
<mat-autocomplete #autoCategorie="matAutocomplete" [displayWith]="displayCategorieFn">
|
||||
@for (option of filteredOptions.categories | async; track option) {
|
||||
<mat-option [value]="option" (onSelectionChange)="onSelCategorie(option)">{{option.categorie}}{{option.categorie && option.module ? ' - ' : ''}}{{option.module}}</mat-option>
|
||||
}
|
||||
</mat-autocomplete>
|
||||
@if (this.jumpForm.controls['categorie'].status === 'INVALID') {
|
||||
<mat-error>{{getErrorMessage('categorie')}}</mat-error>
|
||||
}
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="fill" class="me-2" color="warn">
|
||||
<mat-label>Module</mat-label>
|
||||
<input matInput formControlName="module" [matAutocomplete]="autoModule">
|
||||
<mat-autocomplete #autoModule="matAutocomplete" [displayWith]="displayModuleFn">
|
||||
@for (option of filteredOptions.modules | async; track option) {
|
||||
<mat-option [value]="option" (onSelectionChange)="onSelModule(option)">{{option.categorie}}{{option.categorie && option.module ? ' - ' : ''}}{{option.module}}</mat-option>
|
||||
}
|
||||
</mat-autocomplete>
|
||||
@if (this.jumpForm.controls['module'].status === 'INVALID') {
|
||||
<mat-error>{{getErrorMessage('module')}}</mat-error>
|
||||
}
|
||||
</mat-form-field>
|
||||
<h4 class="border-bottom border-secondary mb-3 lh-lg">
|
||||
Divers
|
||||
<mat-checkbox class="float-end" (change)="setLastDivers($event.checked)" [checked]="useLast.divers">
|
||||
<mat-icon fontIcon="history" class="align-middle"></mat-icon>
|
||||
</mat-checkbox>
|
||||
</h4>
|
||||
<mat-form-field appearance="fill" class="me-2" color="warn">
|
||||
<mat-label>Accessoires</mat-label>
|
||||
<input matInput formControlName="accessoires">
|
||||
@if (this.jumpForm.controls['accessoires'].status === 'INVALID') {
|
||||
<mat-error>{{getErrorMessage('accessoires')}}</mat-error>
|
||||
}
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="fill" class="me-2" color="warn">
|
||||
<mat-label>Zone</mat-label>
|
||||
<input matInput formControlName="zone">
|
||||
@if (this.jumpForm.controls['zone'].status === 'INVALID') {
|
||||
<mat-error>{{getErrorMessage('zone')}}</mat-error>
|
||||
}
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="col-lg-6 col-md-12">
|
||||
<h4 class="border-bottom border-secondary mb-3 lh-lg">
|
||||
Groupe
|
||||
<mat-checkbox class="float-end" (change)="setLastGroupe($event.checked)" [checked]="useLast.groupe">
|
||||
<mat-icon fontIcon="history" class="align-middle"></mat-icon>
|
||||
</mat-checkbox>
|
||||
</h4>
|
||||
<mat-form-field appearance="fill" class="me-2" color="warn">
|
||||
<mat-label>Sautants</mat-label>
|
||||
<input matInput placeholder="Ajouter un sautant" [formControl]="tagField" (keyup.enter)="addSautant()">
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="fill" class="me-2" color="warn">
|
||||
<mat-label>Nombre de participants</mat-label>
|
||||
<input matInput formControlName="participants" type="number" min="0" step="1">
|
||||
@if (this.jumpForm.controls['participants'].status === 'INVALID') {
|
||||
<mat-error>{{getErrorMessage('participants')}}</mat-error>
|
||||
}
|
||||
</mat-form-field>
|
||||
<div class="mb-2">
|
||||
@for (sautant of jump.sautants; track sautant) {
|
||||
<span class="badge rounded-pill bg-primary mb-2 me-1 pe-3 py-1 fw-normal text-dark">
|
||||
<mat-icon class="align-middle pointer me-1" fontIcon="close" (click)="removeSautant(sautant)"></mat-icon>
|
||||
{{ sautant }}
|
||||
</span>
|
||||
}
|
||||
<span class="badge rounded-pill bg-accent mb-2 me-1 pe-3 py-1 fw-normal text-dark">
|
||||
<mat-icon class="align-middle pointer me-1" fontIcon="person"></mat-icon>
|
||||
{{ lastJump.author.username }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6 col-md-12">
|
||||
<h4 class="border-bottom border-secondary mb-3 lh-lg">
|
||||
Programme
|
||||
<mat-checkbox class="float-end" (change)="setLastProgramme($event.checked)" [checked]="useLast.programme">
|
||||
<mat-icon fontIcon="history" class="align-middle"></mat-icon>
|
||||
</mat-checkbox>
|
||||
</h4>
|
||||
<mat-form-field appearance="fill" class="me-2 w-100" color="warn">
|
||||
<mat-label>Programme</mat-label>
|
||||
<textarea matInput formControlName="programme"></textarea>
|
||||
@if (this.jumpForm.controls['programme'].status === 'INVALID') {
|
||||
<mat-error>{{getErrorMessage('programme')}}</mat-error>
|
||||
}
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="col-lg-6 col-md-12">
|
||||
<h4 class="border-bottom border-secondary mb-3 lh-lg">Vidéo</h4>
|
||||
<mat-form-field appearance="fill" class="me-2" color="warn">
|
||||
<mat-label>Dossier</mat-label>
|
||||
<input matInput formControlName="dossier">
|
||||
@if (this.jumpForm.controls['dossier'].status === 'INVALID') {
|
||||
<mat-error>{{getErrorMessage('dossier')}}</mat-error>
|
||||
}
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="fill" class="me-2" color="warn">
|
||||
<mat-label>Video</mat-label>
|
||||
<input matInput formControlName="video">
|
||||
@if (this.jumpForm.controls['video'].status === 'INVALID') {
|
||||
<mat-error>{{getErrorMessage('video')}}</mat-error>
|
||||
}
|
||||
</mat-form-field>
|
||||
<span class="d-inline-block align-middle ms-1">{{ filepath }}{{ filename }}</span>
|
||||
</div>
|
||||
<div class="col-lg-6 col-md-12">
|
||||
<h4 class="border-bottom border-secondary mb-3 lh-lg">Données GPS</h4>
|
||||
<div class="mb-2">
|
||||
<mat-checkbox (change)="setLastJump($event.checked)" [checked]="useLast.all">
|
||||
<mat-icon fontIcon="language" class="align-middle me-2"></mat-icon> <span class="d-inline-block align-middle">Rechercher le fichier kml</span>
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</mat-dialog-content>
|
||||
<mat-dialog-actions>
|
||||
<span class="flex-spacer"></span>
|
||||
<button mat-button (click)="closeDialog()" class="me-1">Annuler</button>
|
||||
<!--<button mat-button color="warn" [disabled]="!jumpForm.valid" [mat-dialog-close]="{jump: jump, params: params}" cdkFocusInitial>Enregistrer</button>-->
|
||||
<button mat-button color="warn" [disabled]="!jumpForm.valid" (click)="submitForm()">Enregistrer</button>
|
||||
</mat-dialog-actions>
|
||||
@@ -0,0 +1,873 @@
|
||||
import { Component, Inject, OnDestroy } from '@angular/core';
|
||||
import { AsyncPipe, DatePipe } from '@angular/common';
|
||||
import { FormBuilder, FormControl, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms';
|
||||
import { MatAutocompleteModule } from '@angular/material/autocomplete';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||
import { MatDatepickerModule } from '@angular/material/datepicker';
|
||||
import { MatDialogRef, MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatOptionModule } from '@angular/material/core';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { Observable, Subscription } from 'rxjs';
|
||||
import { map, startWith, take } from 'rxjs/operators';
|
||||
|
||||
import { AeronefByImat, CanopyModelBySize, DropZoneByOaci, Errors, Jump, JumpAddParams, JumpByModule, JumpFile, JumpFileType } from 'src/app/core/models';
|
||||
import { AeronefsService, CanopiesService, DropZonesService, JumpsService } from 'src/app/core/services';
|
||||
|
||||
@Component({
|
||||
selector: 'app-jump-add-dialog',
|
||||
templateUrl: 'jump-add.dialog.html',
|
||||
standalone: true,
|
||||
imports: [
|
||||
AsyncPipe,
|
||||
DatePipe,
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
MatAutocompleteModule,
|
||||
MatButtonModule,
|
||||
MatCheckboxModule,
|
||||
MatDatepickerModule,
|
||||
MatDialogModule,
|
||||
MatFormFieldModule,
|
||||
MatIconModule,
|
||||
MatInputModule,
|
||||
MatSelectModule,
|
||||
MatOptionModule
|
||||
]
|
||||
})
|
||||
export class JumpAddDialogComponent implements OnDestroy {
|
||||
private _formChanges: Array<Subscription> = [];
|
||||
private _subscriptions: Array<Subscription> = [];
|
||||
private _aeronefByImat: Subscription = new Subscription();
|
||||
private _canopyModelBySize: Subscription = new Subscription();
|
||||
private _dropZoneByOaci: Subscription = new Subscription();
|
||||
private _jumpByModule: Subscription = new Subscription();
|
||||
public errors!: Errors;
|
||||
public tagField = new FormControl<string>('', { nonNullable: true});
|
||||
public jumpForm: FormGroup;
|
||||
public jump: Jump;
|
||||
public lastJump: Jump;
|
||||
public nextJump: number;
|
||||
public params: JumpAddParams;
|
||||
public filename: string;
|
||||
public filepath: string;
|
||||
public useLast: {
|
||||
all: boolean;
|
||||
aeronef: boolean;
|
||||
altitude: boolean;
|
||||
discipline: boolean;
|
||||
divers: boolean;
|
||||
dropzone: boolean;
|
||||
groupe: boolean;
|
||||
programme: boolean;
|
||||
voile: boolean;
|
||||
} = {
|
||||
all: false,
|
||||
aeronef: false,
|
||||
altitude: false,
|
||||
discipline: false,
|
||||
divers: false,
|
||||
dropzone: false,
|
||||
groupe: false,
|
||||
programme: false,
|
||||
voile: false
|
||||
};
|
||||
public inputOptions: {
|
||||
aeronefs: Array<AeronefByImat>;
|
||||
canopies: Array<CanopyModelBySize>;
|
||||
dropzones: Array<DropZoneByOaci>;
|
||||
categories: Array<JumpByModule>;
|
||||
};
|
||||
public filteredOptions: {
|
||||
aeronefs: Observable<Array<AeronefByImat>>;
|
||||
imats: Observable<Array<AeronefByImat>>;
|
||||
canopies: Observable<Array<CanopyModelBySize>>;
|
||||
tailles: Observable<Array<CanopyModelBySize>>;
|
||||
dropzones: Observable<Array<DropZoneByOaci>>;
|
||||
oaci: Observable<Array<DropZoneByOaci>>;
|
||||
categories: Observable<Array<JumpByModule>>;
|
||||
modules: Observable<Array<JumpByModule>>;
|
||||
};
|
||||
|
||||
constructor(
|
||||
public dialogRef: MatDialogRef<JumpAddDialogComponent>,
|
||||
private fb: FormBuilder,
|
||||
private _aeronefsService: AeronefsService,
|
||||
private _canopiesService: CanopiesService,
|
||||
private _dropZonesService: DropZonesService,
|
||||
private _jumpsService: JumpsService,
|
||||
@Inject(MAT_DIALOG_DATA) public data: {jump: Jump, lastJump: Jump}
|
||||
) {
|
||||
this.inputOptions = { aeronefs: [], canopies: [], dropzones: [], categories: [] };
|
||||
//this.filteredOptions.aeronefs = this._aeronefsService.getAllByImat();
|
||||
this.jump = data.jump;
|
||||
this.lastJump = data.lastJump;
|
||||
this.nextJump = (this.lastJump.numero + 1);
|
||||
this.params = {
|
||||
numeros: this.nextJump.toString(),
|
||||
date: '',
|
||||
video: true
|
||||
};
|
||||
this.jump.numero = this.nextJump;
|
||||
const controlsConfig = {
|
||||
date: ['', Validators.required],
|
||||
numero: [this.nextJump, Validators.required],
|
||||
lieu: ['', Validators.required],
|
||||
oaci: '',
|
||||
aeronef: ['', Validators.required],
|
||||
imat: '',
|
||||
hauteur: ['', Validators.required],
|
||||
deploiement: '',
|
||||
voile: '',
|
||||
taille: ['', Validators.required],
|
||||
categorie: '',
|
||||
module: '',
|
||||
participants: [1, Validators.required],
|
||||
sautants: [[]],
|
||||
programme: '',
|
||||
accessoires: '',
|
||||
zone: '',
|
||||
dossier: '',
|
||||
video: ''
|
||||
};
|
||||
this.jumpForm = this.fb.group(controlsConfig);
|
||||
this.filename = this._computeFilename();
|
||||
this.filepath = this._computeFilepath();
|
||||
this._subscriptions.push(
|
||||
this.jumpForm.controls['module'].valueChanges.subscribe(() => {
|
||||
this.filepath = this._computeFilepath();
|
||||
}),
|
||||
this.jumpForm.controls['numero'].valueChanges.subscribe(() => {
|
||||
this.filename = this._computeFilename();
|
||||
})
|
||||
);
|
||||
/*
|
||||
this.filteredAeronefOptions = this.jumpForm.controls['aeronef'].valueChanges.pipe(
|
||||
startWith(''),
|
||||
map(value => {
|
||||
const aeronef = typeof value === 'string' ? value : '';
|
||||
return aeronef ? this._filterString(aeronef as string, this.aeronefOptions) : this.aeronefOptions.slice();
|
||||
}),
|
||||
);
|
||||
this.filteredImatOptions = this.jumpForm.controls['imat'].valueChanges.pipe(
|
||||
startWith(''),
|
||||
map(value => {
|
||||
const imat = typeof value === 'string' ? value : value?.imat;
|
||||
return imat ? this._filterImat(imat as string) : this.imatOptions.slice();
|
||||
}),
|
||||
);
|
||||
//this.filteredAeronefOptions = this._aeronefsService.getAllByImat();
|
||||
*/
|
||||
this.filteredOptions = {
|
||||
aeronefs: this.jumpForm.controls['aeronef'].valueChanges.pipe(
|
||||
startWith(''),
|
||||
map(value => {
|
||||
const aeronef = typeof value === 'string' ? value : value?.aeronef;
|
||||
return aeronef ? this._filterImat(aeronef as string) : this.inputOptions.aeronefs.slice();
|
||||
}),
|
||||
),
|
||||
imats: this.jumpForm.controls['imat'].valueChanges.pipe(
|
||||
startWith(''),
|
||||
map(value => {
|
||||
const imat = typeof value === 'string' ? value : value?.imat;
|
||||
return imat ? this._filterImat(imat as string) : this.inputOptions.aeronefs.slice();
|
||||
}),
|
||||
),
|
||||
canopies: this.jumpForm.controls['voile'].valueChanges.pipe(
|
||||
startWith(''),
|
||||
map(value => {
|
||||
const voile = typeof value === 'string' ? value : value?.voile;
|
||||
return voile ? this._filterTaille(voile as string) : this.inputOptions.canopies.slice();
|
||||
}),
|
||||
),
|
||||
tailles: this.jumpForm.controls['taille'].valueChanges.pipe(
|
||||
startWith(''),
|
||||
map(value => {
|
||||
const taille = typeof value === 'string' ? value : value?.taille.toString();
|
||||
return taille ? this._filterTaille(taille as string) : this.inputOptions.canopies.slice();
|
||||
}),
|
||||
),
|
||||
dropzones: this.jumpForm.controls['lieu'].valueChanges.pipe(
|
||||
startWith(''),
|
||||
map(value => {
|
||||
const lieu = typeof value === 'string' ? value : value?.lieu;
|
||||
return lieu ? this._filterOaci(lieu as string) : this.inputOptions.dropzones.slice();
|
||||
}),
|
||||
),
|
||||
oaci: this.jumpForm.controls['oaci'].valueChanges.pipe(
|
||||
startWith(''),
|
||||
map(value => {
|
||||
const oaci = typeof value === 'string' ? value : value?.oaci;
|
||||
return oaci ? this._filterOaci(oaci as string) : this.inputOptions.dropzones.slice();
|
||||
}),
|
||||
),
|
||||
categories: this.jumpForm.controls['categorie'].valueChanges.pipe(
|
||||
startWith(''),
|
||||
map(value => {
|
||||
const category = typeof value === 'string' ? value : value?.categorie;
|
||||
return category ? this._filterModule(category as string) : this.inputOptions.categories.slice();
|
||||
}),
|
||||
),
|
||||
modules: this.jumpForm.controls['module'].valueChanges.pipe(
|
||||
startWith(''),
|
||||
map(value => {
|
||||
const module = typeof value === 'string' ? value : value?.module;
|
||||
return module ? this._filterModule(module as string) : this.inputOptions.categories.slice();
|
||||
}),
|
||||
)
|
||||
};
|
||||
this._loadAeronefByImat();
|
||||
this._loadCanopyModelBySize();
|
||||
this._loadDropZoneByOaci();
|
||||
this._loadJumpByModule();
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this._subscriptions.forEach((sub: Subscription) => {
|
||||
sub.unsubscribe();
|
||||
});
|
||||
this._aeronefByImat.unsubscribe();
|
||||
this._canopyModelBySize.unsubscribe();
|
||||
this._dropZoneByOaci.unsubscribe();
|
||||
this._jumpByModule.unsubscribe();
|
||||
}
|
||||
|
||||
public onDateChange(): void {
|
||||
this.filename = this._computeFilename();
|
||||
}
|
||||
|
||||
private _computeFilename(numero?: string): string {
|
||||
let file: string = '';
|
||||
const timestamp: number = Date.parse(this.jumpForm.controls['date'].value);
|
||||
if (isNaN(timestamp) == false) {
|
||||
const date: Date = new Date(timestamp);
|
||||
file += `${(date.getFullYear() + '').padStart(2, '0')}-${((date.getMonth() + 1) + '').padStart(2, '0')}-${(date.getDate() + '').padStart(2, '0')}_`;
|
||||
}
|
||||
if (numero !== undefined) {
|
||||
numero = (numero + '').padStart(5, '0');
|
||||
} else {
|
||||
numero = (this.jumpForm.controls['numero'].value + '').padStart(5, '0');
|
||||
}
|
||||
file += `S${numero}.mp4`;
|
||||
this.jumpForm.controls['video'].setValue(file);
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
private _computeFilepath(): string {
|
||||
let path: string = '/Volumes/Storage/Skydive/Videos/';
|
||||
if (this.jumpForm.controls['module'].value != '') {
|
||||
//path += `${this.jumpForm.controls['module'].value}/`;
|
||||
path += typeof this.jumpForm.controls['module'].value === 'string' ? `${this.jumpForm.controls['module'].value}/` : `${this.jumpForm.controls['module'].value?.module}/`;
|
||||
}
|
||||
this.jumpForm.controls['dossier'].setValue(path);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
private _isLastForAllSections() {
|
||||
return (
|
||||
this.useLast.aeronef
|
||||
&& this.useLast.altitude
|
||||
&& this.useLast.discipline
|
||||
&& this.useLast.divers
|
||||
&& this.useLast.dropzone
|
||||
&& this.useLast.groupe
|
||||
&& this.useLast.programme
|
||||
&& this.useLast.voile
|
||||
);
|
||||
}
|
||||
|
||||
private _filterImat(imat: string): Array<AeronefByImat> {
|
||||
const filterValue = imat.toLowerCase();
|
||||
return this.inputOptions.aeronefs.filter(option => (option.aeronef.toLowerCase().includes(filterValue) || option.imat.toLowerCase().includes(filterValue)));
|
||||
}
|
||||
|
||||
private _filterModule(module: string): Array<JumpByModule> {
|
||||
const filterValue = module.toLowerCase();
|
||||
return this.inputOptions.categories.filter(option => (option.categorie.toLowerCase().includes(filterValue) || option.module.toLowerCase().includes(filterValue)));
|
||||
}
|
||||
|
||||
private _filterOaci(oaci: string): Array<DropZoneByOaci> {
|
||||
const filterValue = oaci.toLowerCase();
|
||||
return this.inputOptions.dropzones.filter(option => (option.lieu.toLowerCase().includes(filterValue) || option.oaci.toLowerCase().includes(filterValue)));
|
||||
}
|
||||
|
||||
private _filterTaille(taille: string): Array<CanopyModelBySize> {
|
||||
const filterValue = taille.toLowerCase();
|
||||
return this.inputOptions.canopies.filter(option => (option.voile.toLowerCase().includes(filterValue) || option.taille.toString().includes(filterValue)));
|
||||
}
|
||||
|
||||
private _loadAeronefByImat(): void {
|
||||
const aeronefs$ = this._aeronefsService.getAllByImat();
|
||||
const subscription: Subscription = aeronefs$.subscribe((aggregate: Array<AeronefByImat>) => {
|
||||
/*
|
||||
this.inputOptions.imats = aggregate.map((row: AeronefByImat) => {
|
||||
if (this.inputOptions.aeronefs.indexOf(row.aeronef) === -1) {
|
||||
this.inputOptions.aeronefs.push(row.aeronef);
|
||||
}
|
||||
return row;
|
||||
});
|
||||
*/
|
||||
this.inputOptions.aeronefs = aggregate;
|
||||
});
|
||||
this._subscriptions.push(subscription);
|
||||
}
|
||||
|
||||
private _loadCanopyModelBySize(): void {
|
||||
const canopies$ = this._canopiesService.getAllBySizeByModel();
|
||||
const subscription: Subscription = canopies$.subscribe((aggregate: Array<CanopyModelBySize>) => {
|
||||
/*
|
||||
this.inputOptions.tailles = aggregate.map((row: CanopyModelBySize) => {
|
||||
if (this.inputOptions.canopies.indexOf(row.voile) === -1) {
|
||||
this.inputOptions.canopies.push(row.voile);
|
||||
}
|
||||
return row;
|
||||
});
|
||||
*/
|
||||
this.inputOptions.canopies = aggregate;
|
||||
});
|
||||
this._subscriptions.push(subscription);
|
||||
}
|
||||
|
||||
private _loadDropZoneByOaci(): void {
|
||||
const dropzones$ = this._dropZonesService.getAllByOaci();
|
||||
const subscription: Subscription = dropzones$.subscribe((aggregate: Array<DropZoneByOaci>) => {
|
||||
/*
|
||||
this.inputOptions.oaci = aggregate.map((row: DropZoneByOaci) => {
|
||||
if (this.inputOptions.dropzones.indexOf(row.lieu) === -1) {
|
||||
this.inputOptions.dropzones.push(row.lieu);
|
||||
}
|
||||
return row;
|
||||
});
|
||||
*/
|
||||
this.inputOptions.dropzones = aggregate;
|
||||
});
|
||||
this._subscriptions.push(subscription);
|
||||
}
|
||||
|
||||
private _loadJumpByModule(): void {
|
||||
const jumps$ = this._jumpsService.getAllByModule();
|
||||
const subscription: Subscription = jumps$.subscribe((aggregate: Array<JumpByModule>) => {
|
||||
this.inputOptions.categories = aggregate;
|
||||
});
|
||||
this._subscriptions.push(subscription);
|
||||
}
|
||||
|
||||
private _setAllLast(checked: boolean) {
|
||||
this.useLast.all = checked;
|
||||
this.useLast.aeronef = checked;
|
||||
this.useLast.altitude = checked;
|
||||
this.useLast.discipline = checked;
|
||||
this.useLast.divers = checked;
|
||||
this.useLast.dropzone = checked;
|
||||
this.useLast.groupe = checked;
|
||||
this.useLast.programme = checked;
|
||||
this.useLast.voile = checked;
|
||||
}
|
||||
|
||||
addSautant(): void {
|
||||
this.useLast.groupe = false;
|
||||
this.useLast.all = this._isLastForAllSections();
|
||||
const tag = this.tagField.value;
|
||||
if (tag != null && tag.trim() !== '') {
|
||||
if (tag.includes(',') === true) {
|
||||
const tags = tag.split(',');
|
||||
tags.forEach(sautant => {
|
||||
if (this.jump.sautants.indexOf(sautant.trim()) < 0) {
|
||||
this.jump.sautants.push(sautant.trim());
|
||||
}
|
||||
});
|
||||
} else if (this.jump.sautants.indexOf(tag) < 0) {
|
||||
this.jump.sautants.push(tag);
|
||||
}
|
||||
}
|
||||
this.tagField.reset('');
|
||||
const participants = (this.jump.sautants.length + 1);
|
||||
this.jumpForm.controls['participants'].setValue(participants);
|
||||
}
|
||||
|
||||
closeDialog(): void {
|
||||
this._setAllLast(false);
|
||||
//this.jump = <Jump>{};
|
||||
//this.jump.numero = this.nextJump;
|
||||
this.dialogRef.close();
|
||||
}
|
||||
|
||||
displayAeronefFn(aeronef: AeronefByImat): string {
|
||||
let res = '';
|
||||
if (aeronef) {
|
||||
if (typeof aeronef === 'string') {
|
||||
res = aeronef;
|
||||
} else {
|
||||
res = aeronef.aeronef ? aeronef.aeronef : '';
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
displayImatFn(aeronef: AeronefByImat): string {
|
||||
let res = '';
|
||||
if (aeronef) {
|
||||
if (typeof aeronef === 'string') {
|
||||
res = aeronef;
|
||||
} else {
|
||||
res = aeronef.imat ? aeronef.imat : '';
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
displayLieuFn(dropzone: DropZoneByOaci): string {
|
||||
//return dropzone && dropzone.oaci ? `${dropzone.lieu} - ${dropzone.oaci}` : '';
|
||||
let res = '';
|
||||
if (dropzone) {
|
||||
if (typeof dropzone === 'string') {
|
||||
res = dropzone;
|
||||
} else {
|
||||
res = dropzone.lieu ? dropzone.lieu : '';
|
||||
}
|
||||
}
|
||||
//return dropzone && dropzone.lieu ? dropzone.lieu : '';
|
||||
return res;
|
||||
}
|
||||
|
||||
displayOaciFn(dropzone: DropZoneByOaci): string {
|
||||
//return dropzone && dropzone.oaci ? `${dropzone.lieu} - ${dropzone.oaci}` : '';
|
||||
let res = '';
|
||||
if (dropzone) {
|
||||
if (typeof dropzone === 'string') {
|
||||
res = dropzone;
|
||||
} else {
|
||||
res = dropzone.oaci ? dropzone.oaci : '';
|
||||
}
|
||||
}
|
||||
//return dropzone && dropzone.oaci ? dropzone.oaci : '';
|
||||
return res;
|
||||
}
|
||||
|
||||
displayTailleFn(canopy: CanopyModelBySize): string {
|
||||
let res = '';
|
||||
if (canopy) {
|
||||
if (typeof canopy === 'string') {
|
||||
res = canopy;
|
||||
} else {
|
||||
res = canopy.taille ? canopy.taille.toString() : '';
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
displayVoileFn(canopy: CanopyModelBySize): string {
|
||||
let res = '';
|
||||
if (canopy) {
|
||||
if (typeof canopy === 'string') {
|
||||
res = canopy;
|
||||
} else {
|
||||
res = canopy.voile ? canopy.voile : '';
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
displayCategorieFn(jump: JumpByModule): string {
|
||||
let res = '';
|
||||
if (jump) {
|
||||
if (typeof jump === 'string') {
|
||||
res = jump;
|
||||
} else {
|
||||
res = jump.categorie ? jump.categorie : '';
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
displayModuleFn(jump: JumpByModule): string {
|
||||
let res = '';
|
||||
if (jump) {
|
||||
if (typeof jump === 'string') {
|
||||
res = jump;
|
||||
} else {
|
||||
res = jump.module ? jump.module : '';
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
getErrorMessage(name: string): string {
|
||||
if (this.jumpForm.controls[name].errors !== null) {
|
||||
return 'Ce champ est requis.';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
onSelAeronef(aeronef: AeronefByImat) {
|
||||
if (aeronef && aeronef.aeronef) {
|
||||
//this.jumpForm.controls['imat'].setValue(aeronef.imat);
|
||||
this.jumpForm.controls['imat'].setValue(aeronef);
|
||||
}
|
||||
}
|
||||
|
||||
onSelImat(aeronef: AeronefByImat) {
|
||||
if (aeronef && aeronef.imat) {
|
||||
//this.jumpForm.controls['aeronef'].setValue(aeronef.aeronef);
|
||||
this.jumpForm.controls['aeronef'].setValue(aeronef);
|
||||
}
|
||||
}
|
||||
|
||||
onSelLieu(dropzone: DropZoneByOaci) {
|
||||
if (dropzone && dropzone.oaci) {
|
||||
//this.jumpForm.controls['oaci'].setValue(dropzone.oaci);
|
||||
this.jumpForm.controls['oaci'].setValue(dropzone);
|
||||
}
|
||||
}
|
||||
|
||||
onSelOaci(dropzone: DropZoneByOaci) {
|
||||
if (dropzone && dropzone.lieu) {
|
||||
//this.jumpForm.controls['lieu'].setValue(dropzone.lieu);
|
||||
this.jumpForm.controls['lieu'].setValue(dropzone);
|
||||
}
|
||||
}
|
||||
|
||||
onSelTaille(canopy: CanopyModelBySize) {
|
||||
if (canopy && canopy.voile) {
|
||||
//this.jumpForm.controls['voile'].setValue(canopy.voile);
|
||||
this.jumpForm.controls['voile'].setValue(canopy);
|
||||
}
|
||||
}
|
||||
|
||||
onSelVoile(canopy: CanopyModelBySize) {
|
||||
if (canopy && canopy.taille) {
|
||||
//this.jumpForm.controls['taille'].setValue(canopy.taille.toString());
|
||||
this.jumpForm.controls['taille'].setValue(canopy);
|
||||
}
|
||||
}
|
||||
|
||||
onSelCategorie(jump: JumpByModule) {
|
||||
if (jump && jump.categorie) {
|
||||
this.jumpForm.controls['module'].setValue(jump);
|
||||
}
|
||||
}
|
||||
|
||||
onSelModule(jump: JumpByModule) {
|
||||
if (jump && jump.module) {
|
||||
this.jumpForm.controls['categorie'].setValue(jump);
|
||||
}
|
||||
}
|
||||
|
||||
removeSautant(index: string): void {
|
||||
this.useLast.groupe = false;
|
||||
this.useLast.all = this._isLastForAllSections();
|
||||
this.jump.sautants = this.jump.sautants.filter((sautant) => sautant !== index);
|
||||
const participants = (this.jump.sautants.length + 1);
|
||||
this.jumpForm.controls['participants'].setValue(participants);
|
||||
}
|
||||
|
||||
setLastJump(checked: boolean) {
|
||||
if (checked) {
|
||||
this._setAllLast(true);
|
||||
this.jumpForm.controls['lieu'].setValue(this.lastJump.lieu);
|
||||
this.jumpForm.controls['oaci'].setValue(this.lastJump.oaci);
|
||||
this.jumpForm.controls['aeronef'].setValue(this.lastJump.aeronef);
|
||||
this.jumpForm.controls['imat'].setValue(this.lastJump.imat);
|
||||
this.jumpForm.controls['hauteur'].setValue(this.lastJump.hauteur);
|
||||
this.jumpForm.controls['deploiement'].setValue(this.lastJump.deploiement);
|
||||
this.jumpForm.controls['voile'].setValue(this.lastJump.voile);
|
||||
this.jumpForm.controls['taille'].setValue(this.lastJump.taille?.toString());
|
||||
this.jumpForm.controls['categorie'].setValue(this.lastJump.categorie);
|
||||
this.jumpForm.controls['module'].setValue(this.lastJump.module);
|
||||
this.jumpForm.controls['participants'].setValue(this.lastJump.participants);
|
||||
//this.jumpForm.controls['sautants'].setValue(this.lastJump.sautants);
|
||||
this.jumpForm.controls['programme'].setValue(this.lastJump.programme);
|
||||
this.jumpForm.controls['accessoires'].setValue(this.lastJump.accessoires);
|
||||
this.jumpForm.controls['zone'].setValue(this.lastJump.zone);
|
||||
this.jump.sautants = this.lastJump.sautants;
|
||||
/*
|
||||
Object.assign(
|
||||
this.jump,
|
||||
{
|
||||
date: this.lastJump.date,
|
||||
numero: this.lastJump.numero,
|
||||
lieu: this.lastJump.lieu,
|
||||
oaci: this.lastJump.oaci,
|
||||
aeronef: this.lastJump.aeronef,
|
||||
imat: this.lastJump.imat,
|
||||
hauteur: this.lastJump.hauteur,
|
||||
deploiement: this.lastJump.deploiement,
|
||||
voile: this.lastJump.voile,
|
||||
taille: this.lastJump.taille,
|
||||
categorie: this.lastJump.categorie,
|
||||
module: this.lastJump.module,
|
||||
participants: this.lastJump.participants,
|
||||
sautants: this.lastJump.sautants,
|
||||
programme: this.lastJump.programme,
|
||||
accessoires: this.lastJump.accessoires,
|
||||
zone: this.lastJump.zone,
|
||||
}
|
||||
);
|
||||
*/
|
||||
this.jump.date = '';
|
||||
} else {
|
||||
this._setAllLast(false);
|
||||
this.jumpForm.controls['lieu'].setValue('');
|
||||
this.jumpForm.controls['oaci'].setValue('');
|
||||
this.jumpForm.controls['aeronef'].setValue('');
|
||||
this.jumpForm.controls['imat'].setValue('');
|
||||
this.jumpForm.controls['hauteur'].setValue('');
|
||||
this.jumpForm.controls['deploiement'].setValue('');
|
||||
this.jumpForm.controls['voile'].setValue('');
|
||||
this.jumpForm.controls['taille'].setValue('');
|
||||
this.jumpForm.controls['categorie'].setValue('');
|
||||
this.jumpForm.controls['module'].setValue('');
|
||||
this.jumpForm.controls['participants'].setValue(1);
|
||||
//this.jumpForm.controls['sautants'].setValue('');
|
||||
this.jumpForm.controls['programme'].setValue('');
|
||||
this.jumpForm.controls['accessoires'].setValue('');
|
||||
this.jumpForm.controls['zone'].setValue('');
|
||||
this.jump.sautants = [];
|
||||
}
|
||||
this.jump.numero = this.nextJump;
|
||||
}
|
||||
|
||||
setLastAeronef(checked: boolean = false) {
|
||||
if (checked) {
|
||||
this.useLast.aeronef = true;
|
||||
this.jumpForm.controls['aeronef'].setValue(this.lastJump.aeronef);
|
||||
this.jumpForm.controls['imat'].setValue(this.lastJump.imat);
|
||||
} else {
|
||||
this.useLast.aeronef = false;
|
||||
this.jumpForm.controls['aeronef'].setValue('');
|
||||
this.jumpForm.controls['imat'].setValue('');
|
||||
}
|
||||
this.useLast.all = this._isLastForAllSections();
|
||||
}
|
||||
|
||||
setLastAltitude(checked: boolean = false) {
|
||||
if (checked) {
|
||||
this.useLast.altitude = true;
|
||||
this.jumpForm.controls['hauteur'].setValue(this.lastJump.hauteur);
|
||||
this.jumpForm.controls['deploiement'].setValue(this.lastJump.deploiement);
|
||||
} else {
|
||||
this.useLast.altitude = false;
|
||||
this.jumpForm.controls['hauteur'].setValue('');
|
||||
this.jumpForm.controls['deploiement'].setValue('');
|
||||
}
|
||||
this.useLast.all = this._isLastForAllSections();
|
||||
}
|
||||
|
||||
setLastDiscipline(checked: boolean = false) {
|
||||
if (checked) {
|
||||
this.useLast.discipline = true;
|
||||
this.jumpForm.controls['categorie'].setValue(this.lastJump.categorie);
|
||||
this.jumpForm.controls['module'].setValue(this.lastJump.module);
|
||||
} else {
|
||||
this.useLast.discipline = false;
|
||||
this.jumpForm.controls['categorie'].setValue('');
|
||||
this.jumpForm.controls['module'].setValue('');
|
||||
}
|
||||
this.useLast.all = this._isLastForAllSections();
|
||||
}
|
||||
|
||||
setLastDivers(checked: boolean = false) {
|
||||
if (checked) {
|
||||
this.useLast.divers = true;
|
||||
this.jumpForm.controls['accessoires'].setValue(this.lastJump.accessoires);
|
||||
this.jumpForm.controls['zone'].setValue(this.lastJump.zone);
|
||||
} else {
|
||||
this.useLast.divers = false;
|
||||
this.jumpForm.controls['accessoires'].setValue('');
|
||||
this.jumpForm.controls['zone'].setValue('');
|
||||
}
|
||||
this.useLast.all = this._isLastForAllSections();
|
||||
}
|
||||
|
||||
setLastDropzone(checked: boolean = false) {
|
||||
if (checked) {
|
||||
this.useLast.dropzone = true;
|
||||
this.jumpForm.controls['lieu'].setValue(this.lastJump.lieu);
|
||||
this.jumpForm.controls['oaci'].setValue(this.lastJump.oaci);
|
||||
} else {
|
||||
this.useLast.dropzone = false;
|
||||
this.jumpForm.controls['lieu'].setValue('');
|
||||
this.jumpForm.controls['oaci'].setValue('');
|
||||
}
|
||||
this.useLast.all = this._isLastForAllSections();
|
||||
}
|
||||
|
||||
setLastGroupe(checked: boolean = false) {
|
||||
if (checked) {
|
||||
this.useLast.groupe = true;
|
||||
this.jumpForm.controls['participants'].setValue(this.lastJump.participants);
|
||||
//this.jumpForm.controls['sautants'].setValue(this.lastJump.sautants);
|
||||
this.jump.sautants = this.lastJump.sautants;
|
||||
} else {
|
||||
this.useLast.groupe = false;
|
||||
this.jumpForm.controls['participants'].setValue(1);
|
||||
//this.jumpForm.controls['sautants'].setValue('');
|
||||
this.jump.sautants = [];
|
||||
}
|
||||
this.useLast.all = this._isLastForAllSections();
|
||||
}
|
||||
|
||||
setLastProgramme(checked: boolean = false) {
|
||||
if (checked) {
|
||||
this.useLast.programme = true;
|
||||
this.jumpForm.controls['programme'].setValue(this.lastJump.participants);
|
||||
} else {
|
||||
this.useLast.programme = false;
|
||||
this.jumpForm.controls['programme'].setValue('');
|
||||
}
|
||||
this.useLast.all = this._isLastForAllSections();
|
||||
}
|
||||
|
||||
setLastVoile(checked: boolean = false) {
|
||||
if (checked) {
|
||||
this.useLast.voile = true;
|
||||
this.jumpForm.controls['voile'].setValue(this.lastJump.voile);
|
||||
this.jumpForm.controls['taille'].setValue(this.lastJump.taille?.toString());
|
||||
} else {
|
||||
this.useLast.voile = false;
|
||||
this.jumpForm.controls['voile'].setValue('');
|
||||
this.jumpForm.controls['taille'].setValue('');
|
||||
}
|
||||
this.useLast.all = this._isLastForAllSections();
|
||||
}
|
||||
|
||||
submitForm(): void {
|
||||
/*
|
||||
* Création d'un tableau de saut contenant de 1 à n saut en fonction
|
||||
* de la valeur du champ numero du formulaire jumpForm
|
||||
*/
|
||||
const jumps: Array<Jump> = [];
|
||||
const jump: Jump = {} as Jump;
|
||||
const numeros: Array<number> = [];
|
||||
let date: Date;
|
||||
/* Controle de la validité du champ date et conversion au format ISO */
|
||||
const timestamp: number = Date.parse(this.jumpForm.controls['date'].value);
|
||||
if (isNaN(timestamp) == false) {
|
||||
date = new Date(timestamp);
|
||||
} else {
|
||||
date = new Date();
|
||||
}
|
||||
let value = this.jumpForm.controls['numero'].value;
|
||||
if (typeof value !== 'string') {
|
||||
value = value.toString();
|
||||
}
|
||||
|
||||
if (isNaN(+value) || value.split('+').length == 2) {
|
||||
/*
|
||||
* 'numero' n'est pas un entier , il s'agit donc d'une plage, d'une quantité ou de plusieurs valeur
|
||||
* decomposer la valeur 'numero' et ajouter chaque sauts au tableau retourné par la fenêtre dialog
|
||||
*/
|
||||
let jumpNum: number = this.lastJump.numero;
|
||||
const elements: string[] = this.jumpForm.controls['numero'].value.split(';');
|
||||
elements.forEach((element: string) => {
|
||||
if (element == '') {
|
||||
jumpNum ++;
|
||||
element = jumpNum.toString();
|
||||
}
|
||||
const values: string[] = element.split('-');
|
||||
if (values.length == 2) {
|
||||
const start: number = parseInt(values[0]);
|
||||
const stop: number = parseInt(values[1]);
|
||||
const diff: number = (stop - start);
|
||||
/* Ajout de saut par plage */
|
||||
for (let index = 0; index <= diff; index++) {
|
||||
jumpNum = (start + index);
|
||||
numeros.push(jumpNum);
|
||||
}
|
||||
} else {
|
||||
const quantity: string[] = values[0].split('+');
|
||||
if (quantity.length == 2) {
|
||||
/* Ajout de sauts par quantité */
|
||||
//const start: number = jumpNum;
|
||||
for (let index = 1; index <= parseInt(quantity[1]); index++) {
|
||||
jumpNum ++;
|
||||
numeros.push(jumpNum);
|
||||
}
|
||||
} else {
|
||||
/* Ajout de sauts par numero */
|
||||
jumpNum = parseInt(quantity[0]);
|
||||
numeros.push(jumpNum);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
/* 'numero' est un entier */
|
||||
numeros.push(this.jumpForm.controls['numero'].value);
|
||||
}
|
||||
|
||||
/* Valeurs communes aux 1 à n saut(s) à ajouter */
|
||||
jump.date = date.toISOString();
|
||||
jump.lieu = typeof this.jumpForm.controls['lieu'].value === 'string' ? this.jumpForm.controls['lieu'].value : this.jumpForm.controls['lieu'].value.lieu;
|
||||
jump.oaci = typeof this.jumpForm.controls['oaci'].value === 'string' ? this.jumpForm.controls['oaci'].value : this.jumpForm.controls['oaci'].value.oaci;
|
||||
jump.aeronef = typeof this.jumpForm.controls['aeronef'].value === 'string' ? this.jumpForm.controls['aeronef'].value : this.jumpForm.controls['aeronef'].value.aeronef;
|
||||
jump.imat = typeof this.jumpForm.controls['imat'].value === 'string' ? this.jumpForm.controls['imat'].value : this.jumpForm.controls['imat'].value.imat;
|
||||
jump.voile = typeof this.jumpForm.controls['voile'].value === 'string' ? this.jumpForm.controls['voile'].value : this.jumpForm.controls['voile'].value.voile;
|
||||
jump.taille = typeof this.jumpForm.controls['taille'].value === 'string' ? this.jumpForm.controls['taille'].value : this.jumpForm.controls['taille'].value.taille;
|
||||
jump.categorie = typeof this.jumpForm.controls['categorie'].value === 'string' ? this.jumpForm.controls['categorie'].value : this.jumpForm.controls['categorie'].value.categorie;
|
||||
jump.module = typeof this.jumpForm.controls['module'].value === 'string' ? this.jumpForm.controls['module'].value : this.jumpForm.controls['module'].value.module;
|
||||
jump.hauteur = this.jumpForm.controls['hauteur'].value;
|
||||
jump.deploiement = this.jumpForm.controls['deploiement'].value;
|
||||
jump.participants = this.jumpForm.controls['participants'].value;
|
||||
jump.sautants = this.jump.sautants;
|
||||
//jump.sautants = this.jumpForm.controls['sautants'].value;
|
||||
jump.programme = this.jumpForm.controls['programme'].value;
|
||||
jump.accessoires = this.jumpForm.controls['accessoires'].value;
|
||||
jump.zone = this.jumpForm.controls['zone'].value;
|
||||
//jump.dossier = this.jumpForm.controls['dossier'].value;
|
||||
//jump.video = this.jumpForm.controls['video'].value;
|
||||
|
||||
/* Ajout du saut au tableau à retourner */
|
||||
numeros.forEach((numero: number) => {
|
||||
const item: Jump = {} as Jump;
|
||||
Object.assign(item, jump);
|
||||
item.numero = numero;
|
||||
if (this.jumpForm.controls['dossier'].value !== '' && this.jumpForm.controls['video'].value !== '') {
|
||||
item.dossier = this._computeFilepath();
|
||||
item.video = this._computeFilename(numero.toString());
|
||||
const file: JumpFile = {
|
||||
name: item.video,
|
||||
path: item.dossier,
|
||||
type: JumpFileType.VIDEO
|
||||
} as JumpFile;
|
||||
this._subscriptions.push(
|
||||
this._jumpsService.saveFile(item.slug, file)
|
||||
.pipe(take(1))
|
||||
.subscribe({
|
||||
next: (file) => {
|
||||
item.files.push(file);
|
||||
console.log(`Le fichier ${file.name} a été ajouté au saut n°${item.numero} !`, 'OK');
|
||||
},
|
||||
error: (err) => {
|
||||
this.errors = err;
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
jumps.push(item);
|
||||
//console.log(item);
|
||||
});
|
||||
//console.log(jumps);
|
||||
/* Reset all 'last jump values' booleans */
|
||||
this._setAllLast(false);
|
||||
/* close the dialog with result */
|
||||
this.dialogRef.close(jumps);
|
||||
|
||||
/*
|
||||
// Reset all 'last jump values' booleans
|
||||
this._setAllLast(false);
|
||||
// update the model
|
||||
this.updateJump(this.jumpForm.value);
|
||||
// close the dialog with result
|
||||
this.dialogRef.close(this.jump);
|
||||
*/
|
||||
}
|
||||
|
||||
updateJump(values: NonNullable<unknown>): void {
|
||||
Object.assign(this.jump, values);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<h2 mat-dialog-title class="bg-danger border-bottom border-light-subtle d-flex mb-3 pt-3 pb-2">
|
||||
Supprimer le saut n°{{jump.numero}}<span class="flex-spacer"></span>
|
||||
<mat-icon fontIcon="close" class="pointer" (click)="closeDialog()"></mat-icon>
|
||||
</h2>
|
||||
<mat-dialog-content>
|
||||
<div class="container">
|
||||
<div class="mt-2 mb-4 text-center text-danger border-danger alert alert-danger">
|
||||
<p class="fs-6 m-0">
|
||||
Veuillez confirmer la suppression de ce saut.<br />
|
||||
<span class="fw-semibold">Attention</span>, cette <span class="fw-semibold">action</span> est <span class="fw-semibold">irréversible</span>.
|
||||
</p>
|
||||
</div>
|
||||
<h3 class="border-bottom border-light-subtle pb-2 mb-3">Données du saut :</h3>
|
||||
<div class="row mb-3">
|
||||
<div class="col-lg-6 col-md-12">
|
||||
<dl class="dl-horizontal">
|
||||
<dt class="text-accent fw-normal">numero:</dt>
|
||||
<dd>{{jump.numero}}</dd>
|
||||
<dt class="text-accent fw-normal">lieu:</dt>
|
||||
<dd>{{jump.lieu}}</dd>
|
||||
<dt class="text-accent fw-normal">aeronef:</dt>
|
||||
<dd>{{jump.aeronef}}</dd>
|
||||
<dt class="text-accent fw-normal">voile:</dt>
|
||||
<dd>{{jump.voile}}</dd>
|
||||
<dt class="text-accent fw-normal">hauteur:</dt>
|
||||
<dd>{{jump.hauteur}} m</dd>
|
||||
<dt class="text-accent fw-normal">categorie:</dt>
|
||||
<dd>{{jump.categorie}}</dd>
|
||||
<dt class="text-accent fw-normal">accessoires:</dt>
|
||||
<dd>{{jump.accessoires}}</dd>
|
||||
<dt class="text-accent fw-normal">dossier:</dt>
|
||||
<dd>{{jump.dossier}}</dd>
|
||||
<dt class="text-accent fw-normal">programme:</dt>
|
||||
<dd>{{jump.programme}}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
<div class="col-lg-6 col-md-12">
|
||||
<dl class="dl-horizontal">
|
||||
<dt class="text-accent fw-normal">date:</dt>
|
||||
<dd>{{jump.date | date: 'mediumDate'}}</dd>
|
||||
<dt class="text-accent fw-normal">oaci:</dt>
|
||||
<dd>{{jump.oaci}}</dd>
|
||||
<dt class="text-accent fw-normal">imat:</dt>
|
||||
<dd>{{jump.imat}}</dd>
|
||||
<dt class="text-accent fw-normal">taille:</dt>
|
||||
<dd>{{jump.taille}} ft<sup>2</sup></dd>
|
||||
<dt class="text-accent fw-normal">participants:</dt>
|
||||
<dd>{{jump.participants}}</dd>
|
||||
<dt class="text-accent fw-normal">module:</dt>
|
||||
<dd>{{jump.module}}</dd>
|
||||
<dt class="text-accent fw-normal">zone:</dt>
|
||||
<dd>{{jump.zone}}</dd>
|
||||
<dt class="text-accent fw-normal">video:</dt>
|
||||
<dd>{{jump.video}}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</mat-dialog-content>
|
||||
<mat-dialog-actions>
|
||||
<span class="flex-spacer"></span>
|
||||
<button mat-button (click)="closeDialog()" class="me-1">Annuler</button>
|
||||
<button mat-button color="accent" [mat-dialog-close]="jump" cdkFocusInitial>Supprimer</button>
|
||||
</mat-dialog-actions>
|
||||
@@ -0,0 +1,27 @@
|
||||
import { Component, Inject } from '@angular/core';
|
||||
import { DatePipe } from '@angular/common';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatDialogRef, MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
|
||||
import { Jump } from 'src/app/core/models';
|
||||
|
||||
@Component({
|
||||
selector: 'app-jump-delete-dialog',
|
||||
templateUrl: 'jump-delete.dialog.html',
|
||||
standalone: true,
|
||||
imports: [
|
||||
DatePipe,
|
||||
MatDialogModule, MatButtonModule, MatIconModule
|
||||
]
|
||||
})
|
||||
export class JumpDeleteDialogComponent {
|
||||
constructor(
|
||||
public dialogRef: MatDialogRef<JumpDeleteDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public jump: Jump
|
||||
) { }
|
||||
|
||||
closeDialog(): void {
|
||||
this.dialogRef.close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
<h2 mat-dialog-title class="bg-primary border-bottom border-light-subtle d-flex mb-3 pt-3 pb-2">
|
||||
Modifier un saut<span class="flex-spacer"></span>
|
||||
<mat-icon fontIcon="close" class="pointer" (click)="closeDialog()"></mat-icon>
|
||||
</h2>
|
||||
<mat-dialog-content>
|
||||
<div class="container">
|
||||
<form class="row" [formGroup]="jumpForm" (ngSubmit)="submitForm()">
|
||||
<div class="col-xs-12">
|
||||
<mat-form-field appearance="outline" class="mt-2 me-2" color="primary">
|
||||
<mat-label>Numéro du saut</mat-label>
|
||||
<input matInput formControlName="numero">
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="outline" class="mt-2 me-3" color="primary">
|
||||
<mat-label>Choisissez une date</mat-label>
|
||||
<input matInput [matDatepicker]="datepicker" formControlName="date" />
|
||||
<mat-hint>MM/DD/YYYY</mat-hint>
|
||||
<mat-datepicker-toggle matIconSuffix [for]="datepicker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #datepicker>
|
||||
<mat-datepicker-actions>
|
||||
<button mat-button matDatepickerCancel>Annuler</button>
|
||||
<button mat-raised-button color="primary" matDatepickerApply>Appliquer</button>
|
||||
</mat-datepicker-actions>
|
||||
</mat-datepicker>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="col-lg-6 col-md-12">
|
||||
<h4 class="border-bottom border-primary pb-1 mb-3">Dropzone</h4>
|
||||
<mat-form-field appearance="fill" class="me-2">
|
||||
<mat-label>Lieu</mat-label>
|
||||
<input matInput formControlName="lieu" [matAutocomplete]="autoDropzone">
|
||||
<mat-autocomplete #autoDropzone="matAutocomplete" [displayWith]="displayLieuFn">
|
||||
@for (option of filteredOptions.dropzones | async; track option) {
|
||||
<mat-option [value]="option" (onSelectionChange)="onSelLieu(option)">{{option.lieu}} - {{option.oaci}}</mat-option>
|
||||
}
|
||||
</mat-autocomplete>
|
||||
@if (this.jumpForm.controls['lieu'].status === 'INVALID') {
|
||||
<mat-error>{{getErrorMessage('lieu')}}</mat-error>
|
||||
}
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="fill" class="me-2">
|
||||
<mat-label>Oaci</mat-label>
|
||||
<input matInput formControlName="oaci" [matAutocomplete]="autoOaci">
|
||||
<mat-autocomplete #autoOaci="matAutocomplete" [displayWith]="displayOaciFn">
|
||||
@for (option of filteredOptions.oaci | async; track option) {
|
||||
<mat-option [value]="option" (onSelectionChange)="onSelOaci(option)">{{option.lieu}} - {{option.oaci}}</mat-option>
|
||||
}
|
||||
</mat-autocomplete>
|
||||
@if (this.jumpForm.controls['oaci'].status === 'INVALID') {
|
||||
<mat-error>{{getErrorMessage('oaci')}}</mat-error>
|
||||
}
|
||||
</mat-form-field>
|
||||
<h4 class="border-bottom border-primary pb-1 mb-3">Aéronef</h4>
|
||||
<mat-form-field appearance="fill" class="me-2">
|
||||
<mat-label>Aéronef</mat-label>
|
||||
<input matInput formControlName="aeronef" [matAutocomplete]="autoAeronef">
|
||||
<mat-autocomplete #autoAeronef="matAutocomplete" [displayWith]="displayAeronefFn">
|
||||
@for (option of filteredOptions.aeronefs | async; track option) {
|
||||
<mat-option [value]="option" (onSelectionChange)="onSelAeronef(option)">{{option.aeronef}} - {{option.imat}}</mat-option>
|
||||
}
|
||||
</mat-autocomplete>
|
||||
@if (this.jumpForm.controls['aeronef'].status === 'INVALID') {
|
||||
<mat-error>{{getErrorMessage('aeronef')}}</mat-error>
|
||||
}
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="fill" class="me-2">
|
||||
<mat-label>Imatriculation</mat-label>
|
||||
<input matInput formControlName="imat" [matAutocomplete]="autoImat">
|
||||
<mat-autocomplete #autoImat="matAutocomplete" [displayWith]="displayImatFn">
|
||||
@for (option of filteredOptions.imats | async; track option) {
|
||||
<mat-option [value]="option" (onSelectionChange)="onSelImat(option)">{{option.aeronef}} - {{option.imat}}</mat-option>
|
||||
}
|
||||
</mat-autocomplete>
|
||||
@if (this.jumpForm.controls['imat'].status === 'INVALID') {
|
||||
<mat-error>{{getErrorMessage('imat')}}</mat-error>
|
||||
}
|
||||
</mat-form-field>
|
||||
<h4 class="border-bottom border-primary pb-1 mb-3">Voile</h4>
|
||||
<mat-form-field appearance="fill" class="me-2">
|
||||
<mat-label>Modèle</mat-label>
|
||||
<input matInput formControlName="voile" [matAutocomplete]="autoCanopy">
|
||||
<mat-autocomplete #autoCanopy="matAutocomplete" [displayWith]="displayVoileFn">
|
||||
@for (option of filteredOptions.canopies | async; track option) {
|
||||
<mat-option [value]="option" (onSelectionChange)="onSelVoile(option)">{{option.voile}} - {{option.taille}}</mat-option>
|
||||
}
|
||||
</mat-autocomplete>
|
||||
@if (this.jumpForm.controls['voile'].status === 'INVALID') {
|
||||
<mat-error>{{getErrorMessage('voile')}}</mat-error>
|
||||
}
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="fill" class="me-2">
|
||||
<mat-label>Taille en ft<sup>2</sup></mat-label>
|
||||
<input matInput formControlName="taille" [matAutocomplete]="autoTaille">
|
||||
<mat-autocomplete #autoTaille="matAutocomplete" [displayWith]="displayTailleFn">
|
||||
@for (option of filteredOptions.tailles | async; track option) {
|
||||
<mat-option [value]="option" (onSelectionChange)="onSelTaille(option)">{{option.voile}} - {{option.taille}}</mat-option>
|
||||
}
|
||||
</mat-autocomplete>
|
||||
<span matTextSuffix>ft<sup>2</sup></span>
|
||||
@if (this.jumpForm.controls['taille'].status === 'INVALID') {
|
||||
<mat-error>{{getErrorMessage('taille')}}</mat-error>
|
||||
}
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="col-lg-6 col-md-12">
|
||||
<h4 class="border-bottom border-primary pb-1 mb-3">Altitude</h4>
|
||||
<mat-form-field appearance="fill" class="me-2">
|
||||
<mat-label>Hauteur de largage</mat-label>
|
||||
<input matInput formControlName="hauteur" type="number" min="0" step="1">
|
||||
@if (this.jumpForm.controls['hauteur'].status === 'INVALID') {
|
||||
<mat-error>{{getErrorMessage('hauteur')}}</mat-error>
|
||||
}
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="fill" class="me-2">
|
||||
<mat-label>Hauteur de déploiement</mat-label>
|
||||
<input matInput formControlName="deploiement" type="number" min="0" step="1">
|
||||
@if (this.jumpForm.controls['deploiement'].status === 'INVALID') {
|
||||
<mat-error>{{getErrorMessage('deploiement')}}</mat-error>
|
||||
}
|
||||
</mat-form-field>
|
||||
<h4 class="border-bottom border-primary pb-1 mb-3">Discipline</h4>
|
||||
<mat-form-field appearance="fill" class="me-2">
|
||||
<mat-label>Categorie</mat-label>
|
||||
<input matInput formControlName="categorie" [matAutocomplete]="autoCategorie">
|
||||
<mat-autocomplete #autoCategorie="matAutocomplete" [displayWith]="displayCategorieFn">
|
||||
@for (option of filteredOptions.categories | async; track option) {
|
||||
<mat-option [value]="option" (onSelectionChange)="onSelCategorie(option)">{{option.categorie}}{{option.categorie && option.module ? ' - ' : ''}}{{option.module}}</mat-option>
|
||||
}
|
||||
</mat-autocomplete>
|
||||
@if (this.jumpForm.controls['categorie'].status === 'INVALID') {
|
||||
<mat-error>{{getErrorMessage('categorie')}}</mat-error>
|
||||
}
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="fill" class="me-2">
|
||||
<mat-label>Module</mat-label>
|
||||
<input matInput formControlName="module" [matAutocomplete]="autoModule">
|
||||
<mat-autocomplete #autoModule="matAutocomplete" [displayWith]="displayModuleFn">
|
||||
@for (option of filteredOptions.modules | async; track option) {
|
||||
<mat-option [value]="option" (onSelectionChange)="onSelModule(option)">{{option.categorie}}{{option.categorie && option.module ? ' - ' : ''}}{{option.module}}</mat-option>
|
||||
}
|
||||
</mat-autocomplete>
|
||||
@if (this.jumpForm.controls['module'].status === 'INVALID') {
|
||||
<mat-error>{{getErrorMessage('module')}}</mat-error>
|
||||
}
|
||||
</mat-form-field>
|
||||
<h4 class="border-bottom border-primary pb-1 mb-3">Divers</h4>
|
||||
<mat-form-field appearance="fill" class="me-2">
|
||||
<mat-label>Accessoires</mat-label>
|
||||
<input matInput formControlName="accessoires">
|
||||
@if (this.jumpForm.controls['accessoires'].status === 'INVALID') {
|
||||
<mat-error>{{getErrorMessage('accessoires')}}</mat-error>
|
||||
}
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="fill" class="me-2">
|
||||
<mat-label>Zone</mat-label>
|
||||
<input matInput formControlName="zone">
|
||||
@if (this.jumpForm.controls['zone'].status === 'INVALID') {
|
||||
<mat-error>{{getErrorMessage('zone')}}</mat-error>
|
||||
}
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="col-lg-6 col-md-12">
|
||||
<h4 class="border-bottom border-primary pb-1 mb-3">Groupe</h4>
|
||||
<mat-form-field appearance="fill" class="me-2">
|
||||
<mat-label>Sautants</mat-label>
|
||||
<input matInput placeholder="Ajouter un sautant" [formControl]="tagField" (keyup.enter)="addSautant()">
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="fill" class="me-2">
|
||||
<mat-label>Nombre de participants</mat-label>
|
||||
<input matInput formControlName="participants" type="number" min="0" step="1">
|
||||
@if (this.jumpForm.controls['participants'].status === 'INVALID') {
|
||||
<mat-error>{{getErrorMessage('participants')}}</mat-error>
|
||||
}
|
||||
</mat-form-field>
|
||||
<div class="mb-2">
|
||||
@for (sautant of jump.sautants; track sautant) {
|
||||
<span class="badge rounded-pill bg-primary mb-2 me-1 pe-3 py-1 fw-normal text-dark">
|
||||
<mat-icon class="align-middle pointer me-1" fontIcon="close" (click)="removeSautant(sautant)"></mat-icon>
|
||||
{{ sautant }}
|
||||
</span>
|
||||
}
|
||||
<span class="badge rounded-pill bg-accent mb-2 me-1 pe-3 py-1 fw-normal text-dark">
|
||||
<mat-icon class="align-middle pointer me-1" fontIcon="person"></mat-icon>
|
||||
{{ jump.author.username }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6 col-md-12">
|
||||
<h4 class="border-bottom border-primary pb-1 mb-3">Programme</h4>
|
||||
<mat-form-field appearance="fill" class="me-2 w-100">
|
||||
<mat-label>Programme</mat-label>
|
||||
<textarea matInput formControlName="programme"></textarea>
|
||||
@if (this.jumpForm.controls['programme'].status === 'INVALID') {
|
||||
<mat-error>{{getErrorMessage('programme')}}</mat-error>
|
||||
}
|
||||
</mat-form-field>
|
||||
<h4 class="border-bottom border-primary pb-1 mb-3">Vidéo</h4>
|
||||
<mat-form-field appearance="fill" class="me-2">
|
||||
<mat-label>Dossier</mat-label>
|
||||
<input matInput formControlName="dossier">
|
||||
@if (this.jumpForm.controls['dossier'].status === 'INVALID') {
|
||||
<mat-error>{{getErrorMessage('dossier')}}</mat-error>
|
||||
}
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="fill" class="me-2">
|
||||
<mat-label>Video</mat-label>
|
||||
<input matInput formControlName="video">
|
||||
@if (this.jumpForm.controls['video'].status === 'INVALID') {
|
||||
<mat-error>{{getErrorMessage('video')}}</mat-error>
|
||||
}
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</mat-dialog-content>
|
||||
<mat-dialog-actions>
|
||||
<span class="flex-spacer"></span>
|
||||
<button mat-button (click)="closeDialog()" class="me-1">Annuler</button>
|
||||
<button mat-button color="primary" [disabled]="!jumpForm.valid" (click)="submitForm()" cdkFocusInitial>Enregistrer</button>
|
||||
</mat-dialog-actions>
|
||||
@@ -0,0 +1,407 @@
|
||||
import { Component, Inject, OnDestroy } from '@angular/core';
|
||||
import { AsyncPipe, DatePipe } from '@angular/common';
|
||||
import { FormBuilder, FormControl, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms';
|
||||
import { MatAutocompleteModule } from '@angular/material/autocomplete';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||
import { MatDatepickerModule } from '@angular/material/datepicker';
|
||||
import { MatDialogRef, MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatOptionModule } from '@angular/material/core';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { Observable, Subscription } from 'rxjs';
|
||||
import { map, startWith } from 'rxjs/operators';
|
||||
|
||||
import { AeronefByImat, CanopyModelBySize, DropZoneByOaci, Jump, JumpByModule } from 'src/app/core/models';
|
||||
import { AeronefsService, CanopiesService, DropZonesService, JumpsService } from 'src/app/core/services';
|
||||
|
||||
@Component({
|
||||
selector: 'app-jump-edit-dialog',
|
||||
templateUrl: 'jump-edit.dialog.html',
|
||||
standalone: true,
|
||||
imports: [
|
||||
AsyncPipe,
|
||||
DatePipe,
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
MatAutocompleteModule,
|
||||
MatButtonModule,
|
||||
MatCheckboxModule,
|
||||
MatDatepickerModule,
|
||||
MatDialogModule,
|
||||
MatFormFieldModule,
|
||||
MatIconModule,
|
||||
MatInputModule,
|
||||
MatSelectModule,
|
||||
MatOptionModule
|
||||
]
|
||||
})
|
||||
export class JumpEditDialogComponent implements OnDestroy {
|
||||
private _aeronefByImat: Subscription = new Subscription();
|
||||
private _canopyModelBySize: Subscription = new Subscription();
|
||||
private _dropZoneByOaci: Subscription = new Subscription();
|
||||
private _jumpByModule: Subscription = new Subscription();
|
||||
public tagField = new FormControl<string>('', { nonNullable: true});
|
||||
public jumpForm: FormGroup;
|
||||
public inputOptions: {
|
||||
aeronefs: Array<AeronefByImat>;
|
||||
canopies: Array<CanopyModelBySize>;
|
||||
dropzones: Array<DropZoneByOaci>;
|
||||
categories: Array<JumpByModule>;
|
||||
};
|
||||
public filteredOptions: {
|
||||
aeronefs: Observable<Array<AeronefByImat>>;
|
||||
imats: Observable<Array<AeronefByImat>>;
|
||||
canopies: Observable<Array<CanopyModelBySize>>;
|
||||
tailles: Observable<Array<CanopyModelBySize>>;
|
||||
dropzones: Observable<Array<DropZoneByOaci>>;
|
||||
oaci: Observable<Array<DropZoneByOaci>>;
|
||||
categories: Observable<Array<JumpByModule>>;
|
||||
modules: Observable<Array<JumpByModule>>;
|
||||
};
|
||||
|
||||
constructor(
|
||||
public dialogRef: MatDialogRef<JumpEditDialogComponent>,
|
||||
private fb: FormBuilder,
|
||||
private _aeronefsService: AeronefsService,
|
||||
private _canopiesService: CanopiesService,
|
||||
private _dropZonesService: DropZonesService,
|
||||
private _jumpsService: JumpsService,
|
||||
@Inject(MAT_DIALOG_DATA) public jump: Jump
|
||||
) {
|
||||
this.inputOptions = { aeronefs: [], canopies: [], dropzones: [], categories: [] };
|
||||
const controlsConfig = {
|
||||
slug: [this.jump.slug, Validators.required],
|
||||
date: [this.jump.date, Validators.required],
|
||||
numero: [this.jump.numero, Validators.required],
|
||||
lieu: [this.jump.lieu, Validators.required],
|
||||
oaci: this.jump.oaci,
|
||||
aeronef: [this.jump.aeronef, Validators.required],
|
||||
imat: this.jump.imat,
|
||||
hauteur: [this.jump.hauteur, Validators.required],
|
||||
deploiement: this.jump.deploiement,
|
||||
voile: this.jump.voile,
|
||||
taille: [this.jump.taille?.toString(), Validators.required],
|
||||
categorie: this.jump.categorie,
|
||||
module: this.jump.module,
|
||||
participants: [this.jump.participants, Validators.required],
|
||||
programme: this.jump.programme,
|
||||
accessoires: this.jump.accessoires,
|
||||
zone: this.jump.zone,
|
||||
dossier: this.jump.dossier,
|
||||
video: this.jump.video
|
||||
};
|
||||
this.jumpForm = this.fb.group(controlsConfig);
|
||||
|
||||
this.filteredOptions = {
|
||||
aeronefs: this.jumpForm.controls['aeronef'].valueChanges.pipe(
|
||||
startWith(''),
|
||||
map(value => {
|
||||
const aeronef = typeof value === 'string' ? value : value?.aeronef;
|
||||
return aeronef ? this._filterImat(aeronef as string) : this.inputOptions.aeronefs.slice();
|
||||
}),
|
||||
),
|
||||
imats: this.jumpForm.controls['imat'].valueChanges.pipe(
|
||||
startWith(''),
|
||||
map(value => {
|
||||
const imat = typeof value === 'string' ? value : value?.imat;
|
||||
return imat ? this._filterImat(imat as string) : this.inputOptions.aeronefs.slice();
|
||||
}),
|
||||
),
|
||||
canopies: this.jumpForm.controls['voile'].valueChanges.pipe(
|
||||
startWith(''),
|
||||
map(value => {
|
||||
const voile = typeof value === 'string' ? value : value?.voile;
|
||||
return voile ? this._filterTaille(voile as string) : this.inputOptions.canopies.slice();
|
||||
}),
|
||||
),
|
||||
tailles: this.jumpForm.controls['taille'].valueChanges.pipe(
|
||||
startWith(''),
|
||||
map(value => {
|
||||
const taille = typeof value === 'string' ? value : value?.taille.toString();
|
||||
return taille ? this._filterTaille(taille as string) : this.inputOptions.canopies.slice();
|
||||
}),
|
||||
),
|
||||
dropzones: this.jumpForm.controls['lieu'].valueChanges.pipe(
|
||||
startWith(''),
|
||||
map(value => {
|
||||
const lieu = typeof value === 'string' ? value : value?.lieu;
|
||||
return lieu ? this._filterOaci(lieu as string) : this.inputOptions.dropzones.slice();
|
||||
}),
|
||||
),
|
||||
oaci: this.jumpForm.controls['oaci'].valueChanges.pipe(
|
||||
startWith(''),
|
||||
map(value => {
|
||||
const oaci = typeof value === 'string' ? value : value?.oaci;
|
||||
return oaci ? this._filterOaci(oaci as string) : this.inputOptions.dropzones.slice();
|
||||
}),
|
||||
),
|
||||
categories: this.jumpForm.controls['categorie'].valueChanges.pipe(
|
||||
startWith(''),
|
||||
map(value => {
|
||||
const category = typeof value === 'string' ? value : value?.categorie;
|
||||
return category ? this._filterModule(category as string) : this.inputOptions.categories.slice();
|
||||
}),
|
||||
),
|
||||
modules: this.jumpForm.controls['module'].valueChanges.pipe(
|
||||
startWith(''),
|
||||
map(value => {
|
||||
const module = typeof value === 'string' ? value : value?.module;
|
||||
return module ? this._filterModule(module as string) : this.inputOptions.categories.slice();
|
||||
}),
|
||||
)
|
||||
};
|
||||
this._loadAeronefByImat();
|
||||
this._loadCanopyModelBySize();
|
||||
this._loadDropZoneByOaci();
|
||||
this._loadJumpByModule();
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this._aeronefByImat.unsubscribe();
|
||||
this._canopyModelBySize.unsubscribe();
|
||||
this._dropZoneByOaci.unsubscribe();
|
||||
this._jumpByModule.unsubscribe();
|
||||
}
|
||||
|
||||
private _filterImat(imat: string): Array<AeronefByImat> {
|
||||
const filterValue = imat.toLowerCase();
|
||||
return this.inputOptions.aeronefs.filter(option => (option.aeronef.toLowerCase().includes(filterValue) || option.imat.toLowerCase().includes(filterValue)));
|
||||
}
|
||||
|
||||
private _filterModule(module: string): Array<JumpByModule> {
|
||||
const filterValue = module.toLowerCase();
|
||||
return this.inputOptions.categories.filter(option => (option.categorie.toLowerCase().includes(filterValue) || option.module.toLowerCase().includes(filterValue)));
|
||||
}
|
||||
|
||||
private _filterOaci(oaci: string): Array<DropZoneByOaci> {
|
||||
const filterValue = oaci.toLowerCase();
|
||||
return this.inputOptions.dropzones.filter(option => (option.lieu.toLowerCase().includes(filterValue) || option.oaci.toLowerCase().includes(filterValue)));
|
||||
}
|
||||
|
||||
private _filterTaille(taille: string): Array<CanopyModelBySize> {
|
||||
const filterValue = taille.toLowerCase();
|
||||
return this.inputOptions.canopies.filter(option => (option.voile.toLowerCase().includes(filterValue) || option.taille.toString().includes(filterValue)));
|
||||
}
|
||||
|
||||
private _loadAeronefByImat(): void {
|
||||
const aeronefs$ = this._aeronefsService.getAllByImat();
|
||||
this._aeronefByImat = aeronefs$.subscribe((aggregate: Array<AeronefByImat>) => {
|
||||
this.inputOptions.aeronefs = aggregate;
|
||||
});
|
||||
}
|
||||
|
||||
private _loadCanopyModelBySize(): void {
|
||||
const canopies$ = this._canopiesService.getAllBySizeByModel();
|
||||
this._canopyModelBySize = canopies$.subscribe((aggregate: Array<CanopyModelBySize>) => {
|
||||
this.inputOptions.canopies = aggregate;
|
||||
});
|
||||
}
|
||||
|
||||
private _loadDropZoneByOaci(): void {
|
||||
const dropzones$ = this._dropZonesService.getAllByOaci();
|
||||
this._dropZoneByOaci = dropzones$.subscribe((aggregate: Array<DropZoneByOaci>) => {
|
||||
this.inputOptions.dropzones = aggregate;
|
||||
});
|
||||
}
|
||||
|
||||
private _loadJumpByModule(): void {
|
||||
const jumps$ = this._jumpsService.getAllByModule();
|
||||
this._jumpByModule = jumps$.subscribe((aggregate: Array<JumpByModule>) => {
|
||||
this.inputOptions.categories = aggregate;
|
||||
});
|
||||
}
|
||||
|
||||
addSautant(): void {
|
||||
const tag = this.tagField.value;
|
||||
if (tag != null && tag.trim() !== '' && this.jump.sautants.indexOf(tag) < 0) {
|
||||
this.jump.sautants.push(tag);
|
||||
}
|
||||
const participants = (this.jump.sautants.length + 1);
|
||||
this.jumpForm.controls['participants'].setValue(participants);
|
||||
this.tagField.reset('');
|
||||
}
|
||||
|
||||
closeDialog(): void {
|
||||
// close the dialog without result
|
||||
this.dialogRef.close();
|
||||
}
|
||||
|
||||
displayAeronefFn(aeronef: AeronefByImat): string {
|
||||
let res = '';
|
||||
if (aeronef) {
|
||||
if (typeof aeronef === 'string') {
|
||||
res = aeronef;
|
||||
} else {
|
||||
res = aeronef.aeronef ? aeronef.aeronef : '';
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
displayImatFn(aeronef: AeronefByImat): string {
|
||||
let res = '';
|
||||
if (aeronef) {
|
||||
if (typeof aeronef === 'string') {
|
||||
res = aeronef;
|
||||
} else {
|
||||
res = aeronef.imat ? aeronef.imat : '';
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
displayLieuFn(dropzone: DropZoneByOaci): string {
|
||||
let res = '';
|
||||
if (dropzone) {
|
||||
if (typeof dropzone === 'string') {
|
||||
res = dropzone;
|
||||
} else {
|
||||
res = dropzone.lieu ? dropzone.lieu : '';
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
displayOaciFn(dropzone: DropZoneByOaci): string {
|
||||
let res = '';
|
||||
if (dropzone) {
|
||||
if (typeof dropzone === 'string') {
|
||||
res = dropzone;
|
||||
} else {
|
||||
res = dropzone.oaci ? dropzone.oaci : '';
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
displayTailleFn(canopy: CanopyModelBySize): string {
|
||||
let res = '';
|
||||
if (canopy) {
|
||||
if (typeof canopy === 'string') {
|
||||
res = canopy;
|
||||
} else {
|
||||
res = canopy.taille ? canopy.taille.toString() : '';
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
displayVoileFn(canopy: CanopyModelBySize): string {
|
||||
let res = '';
|
||||
if (canopy) {
|
||||
if (typeof canopy === 'string') {
|
||||
res = canopy;
|
||||
} else {
|
||||
res = canopy.voile ? canopy.voile : '';
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
displayCategorieFn(jump: JumpByModule): string {
|
||||
let res = '';
|
||||
if (jump) {
|
||||
if (typeof jump === 'string') {
|
||||
res = jump;
|
||||
} else {
|
||||
res = jump.categorie ? jump.categorie : '';
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
displayModuleFn(jump: JumpByModule): string {
|
||||
let res = '';
|
||||
if (jump) {
|
||||
if (typeof jump === 'string') {
|
||||
res = jump;
|
||||
} else {
|
||||
res = jump.module ? jump.module : '';
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
getErrorMessage(name: string): string {
|
||||
if (this.jumpForm.controls[name].errors !== null) {
|
||||
return `Ce champ est requis.`;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
onSelAeronef(aeronef: AeronefByImat) {
|
||||
if (aeronef && aeronef.aeronef) {
|
||||
this.jumpForm.controls['imat'].setValue(aeronef);
|
||||
}
|
||||
}
|
||||
|
||||
onSelImat(aeronef: AeronefByImat) {
|
||||
if (aeronef && aeronef.imat) {
|
||||
this.jumpForm.controls['aeronef'].setValue(aeronef);
|
||||
}
|
||||
}
|
||||
|
||||
onSelLieu(dropzone: DropZoneByOaci) {
|
||||
if (dropzone && dropzone.oaci) {
|
||||
this.jumpForm.controls['oaci'].setValue(dropzone);
|
||||
}
|
||||
}
|
||||
|
||||
onSelOaci(dropzone: DropZoneByOaci) {
|
||||
if (dropzone && dropzone.lieu) {
|
||||
this.jumpForm.controls['lieu'].setValue(dropzone);
|
||||
}
|
||||
}
|
||||
|
||||
onSelTaille(canopy: CanopyModelBySize) {
|
||||
if (canopy && canopy.voile) {
|
||||
this.jumpForm.controls['voile'].setValue(canopy);
|
||||
}
|
||||
}
|
||||
|
||||
onSelVoile(canopy: CanopyModelBySize) {
|
||||
if (canopy && canopy.taille) {
|
||||
this.jumpForm.controls['taille'].setValue(canopy);
|
||||
}
|
||||
}
|
||||
|
||||
onSelCategorie(jump: JumpByModule) {
|
||||
if (jump && jump.categorie) {
|
||||
this.jumpForm.controls['module'].setValue(jump);
|
||||
}
|
||||
}
|
||||
|
||||
onSelModule(jump: JumpByModule) {
|
||||
if (jump && jump.module) {
|
||||
this.jumpForm.controls['categorie'].setValue(jump);
|
||||
}
|
||||
}
|
||||
|
||||
removeSautant(index: string): void {
|
||||
this.jump.sautants = this.jump.sautants.filter((sautant) => sautant !== index);
|
||||
const participants = (this.jump.sautants.length + 1);
|
||||
this.jumpForm.controls['participants'].setValue(participants);
|
||||
}
|
||||
|
||||
submitForm(): void {
|
||||
// update the model
|
||||
this.updateJump(this.jumpForm.value);
|
||||
this.jump.lieu = typeof this.jumpForm.controls['lieu'].value === 'string' ? this.jumpForm.controls['lieu'].value : this.jumpForm.controls['lieu'].value.lieu;
|
||||
this.jump.oaci = typeof this.jumpForm.controls['oaci'].value === 'string' ? this.jumpForm.controls['oaci'].value : this.jumpForm.controls['oaci'].value.oaci;
|
||||
this.jump.aeronef = typeof this.jumpForm.controls['aeronef'].value === 'string' ? this.jumpForm.controls['aeronef'].value : this.jumpForm.controls['aeronef'].value.aeronef;
|
||||
this.jump.imat = typeof this.jumpForm.controls['imat'].value === 'string' ? this.jumpForm.controls['imat'].value : this.jumpForm.controls['imat'].value.imat;
|
||||
this.jump.voile = typeof this.jumpForm.controls['voile'].value === 'string' ? this.jumpForm.controls['voile'].value : this.jumpForm.controls['voile'].value.voile;
|
||||
this.jump.taille = typeof this.jumpForm.controls['taille'].value === 'string' ? this.jumpForm.controls['taille'].value : this.jumpForm.controls['taille'].value.taille;
|
||||
this.jump.categorie = typeof this.jumpForm.controls['categorie'].value === 'string' ? this.jumpForm.controls['categorie'].value : this.jumpForm.controls['categorie'].value.categorie;
|
||||
this.jump.module = typeof this.jumpForm.controls['module'].value === 'string' ? this.jumpForm.controls['module'].value : this.jumpForm.controls['module'].value.module;
|
||||
// close the dialog with result
|
||||
this.dialogRef.close(this.jump);
|
||||
}
|
||||
|
||||
updateJump(values: NonNullable<unknown>): void {
|
||||
Object.assign(this.jump, values);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
<h2 mat-dialog-title class="bg-navy-light border-bottom border-light-subtle d-flex mb-3 pt-3 pb-2">
|
||||
Saut n° {{jump.numero}}<span class="flex-spacer"></span>
|
||||
<mat-icon fontIcon="close" class="pointer" (click)="closeDialog()"></mat-icon>
|
||||
</h2>
|
||||
<mat-dialog-content>
|
||||
<div class="container">
|
||||
<div class="d-flex border-bottom border-light-subtle text-light-info pb-2 mb-3">
|
||||
<div class="flex-fill">
|
||||
<h5>Le {{ jump.date | date: 'dd/MM/yyyy' }} à {{ jump.lieu }} {{ jump.zone ? jump.zone : jump.oaci }} :</h5>
|
||||
</div>
|
||||
<span class="flex-spacer"></span>
|
||||
<div class="align-self-center me-2">
|
||||
<span class="text-accent fs-5">{{ jump.categorie }}</span>
|
||||
@if (jump.module) {
|
||||
<span class="fs-5 mx-2">/</span>
|
||||
<span class="text-muted fs-5">{{ jump.module }}</span>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-6 col-md-12">
|
||||
<dl class="dl-horizontal">
|
||||
<dt class="text-light-info fw-normal">Aeronef:</dt>
|
||||
<dd>{{ jump.aeronef }} {{ jump.imat }}</dd>
|
||||
<dt class="text-light-info fw-normal">Voile:</dt>
|
||||
<dd>{{ jump.voile }} {{ jump.taille }} ft<sup>2</sup></dd>
|
||||
@if (jump.programme) {
|
||||
<dt class="text-light-info fw-normal">Programme:</dt>
|
||||
<dd>{{ jump.programme }}</dd>
|
||||
}
|
||||
</dl>
|
||||
</div>
|
||||
<div class="col-lg-6 col-md-12">
|
||||
<dl class="dl-horizontal">
|
||||
@if (!jump.x2data) {
|
||||
<dt class="text-light-info fw-normal">Hauteur:</dt>
|
||||
<dd>{{ jump.hauteur }} m</dd>
|
||||
<dt class="text-light-info fw-normal">Déploiement:</dt>
|
||||
<dd>{{ jump.deploiement }} m</dd>
|
||||
}
|
||||
@for (file of jump.files; track file) {
|
||||
<dt class="text-light-info fw-normal">Fichier {{ file.type }}</dt>
|
||||
<dd>{{ file.name }}</dd>
|
||||
}
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
<mat-divider></mat-divider>
|
||||
@if (jump.x2data) {
|
||||
<div class="row my-3">
|
||||
<div class="col-sm-4 col-xs-12 text-center">
|
||||
<h6>Hauteur</h6>
|
||||
<span class="text-primary fw-medium fs-5">{{ jump.x2data.altitude.exit }} m</span>
|
||||
</div>
|
||||
<div class="col-sm-4 col-xs-12 text-center">
|
||||
<h6 class="d-flex justify-content-center"><mat-icon fontIcon="timer" class="me-1 fs-5"></mat-icon> Durée de la chute</h6>
|
||||
<span class="text-primary fw-medium fs-5">{{formatDuration(jump.x2data.duration.freeFall)}}</span>
|
||||
</div>
|
||||
<div class="col-sm-4 col-xs-12 text-center">
|
||||
<h6>Déploiement</h6>
|
||||
<span class="text-primary fw-medium fs-5">{{ jump.x2data.altitude.deployment }} m</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row my-4">
|
||||
<div class="{{ jump.accessoires ? 'col-sm-6 ' : '' }}col-xs-12 text-center">
|
||||
<h5 class="d-flex justify-content-center mb-4"><mat-icon fontIcon="people" class="me-1"></mat-icon> Participants ({{ jump.sautants.length + 1 }})</h5>
|
||||
@for (sautant of jump.sautants; track sautant) {
|
||||
<span class="badge rounded-pill bg-primary mb-2 me-1 pe-3 py-1 fw-normal text-dark">
|
||||
<mat-icon class="align-middle me-1" fontIcon="person"></mat-icon>
|
||||
{{ sautant }}
|
||||
</span>
|
||||
}
|
||||
<span class="badge rounded-pill bg-accent mb-2 me-1 pe-3 py-1 fw-normal text-dark">
|
||||
<mat-icon class="align-middle me-1" fontIcon="person"></mat-icon>
|
||||
{{ jump.author.username }}
|
||||
</span>
|
||||
</div>
|
||||
@if (jump.accessoires) {
|
||||
<div class="col-sm-6 col-xs-12 text-center">
|
||||
<h5 class="d-flex justify-content-center mb-4"><mat-icon fontIcon="auto_awesome" class="me-1"></mat-icon> Accessoires</h5>
|
||||
<span class="badge rounded-pill bg-navy-light mb-2 me-1 pe-3 py-1 fw-normal">
|
||||
<mat-icon class="align-middle me-1" fontIcon="auto_awesome"></mat-icon>
|
||||
{{ jump.accessoires }}
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="row my-3">
|
||||
<div class="col-xs-12 text-center">
|
||||
<h5 class="d-flex justify-content-center mb-3"><mat-icon fontIcon="speed" class="me-1"></mat-icon> Vitesse en chute</h5>
|
||||
</div>
|
||||
<div class="col-sm-3 col-xs-12 text-center">
|
||||
<h6>Max Verticale</h6>
|
||||
<span class="text-primary fw-medium fs-5">{{ (jump.x2data.speed.freeFall.vertical.max / 1000 * 60 * 60) | number : '1.0-0' }} km/h</span>
|
||||
</div>
|
||||
<div class="col-sm-3 col-xs-12 text-center">
|
||||
<h6>Max Horizontale</h6>
|
||||
<span class="text-primary fw-medium fs-5">{{ (jump.x2data.speed.freeFall.horizontal.max / 1000 * 60 * 60) | number : '1.0-0' }} km/h</span>
|
||||
</div>
|
||||
<div class="col-sm-3 col-xs-12 text-center">
|
||||
<h6>Moyenne Verticale</h6>
|
||||
<span class="text-primary fw-medium fs-5">{{ (jump.x2data.speed.freeFall.vertical.avg / 1000 * 60 * 60) | number : '1.0-0' }} km/h</span>
|
||||
</div>
|
||||
<div class="col-sm-3 col-xs-12 text-center">
|
||||
<h6>Moyenne Horizontale</h6>
|
||||
<span class="text-primary fw-medium fs-5">{{ (jump.x2data.speed.freeFall.horizontal.avg / 1000 * 60 * 60) | number : '1.0-0' }} km/h</span>
|
||||
</div>
|
||||
</div>
|
||||
<mat-divider></mat-divider>
|
||||
|
||||
<div class="row mt-4 mb-3">
|
||||
<div class="col-xs-12 text-center">
|
||||
<h5 class="d-flex justify-content-center mb-3"><mat-icon fontIcon="speed" class="me-1"></mat-icon> Vitesse sous voile</h5>
|
||||
</div>
|
||||
<div class="col-sm-3 col-xs-12 text-center">
|
||||
<h6>Max Verticale</h6>
|
||||
<span class="text-primary fw-medium fs-5">{{ (jump.x2data.speed.underCanopy.vertical.max / 1000 * 60 * 60) | number : '1.0-0' }} km/h</span>
|
||||
</div>
|
||||
<div class="col-sm-3 col-xs-12 text-center">
|
||||
<h6>Max Horizontale</h6>
|
||||
<span class="text-primary fw-medium fs-5">{{ (jump.x2data.speed.underCanopy.horizontal.max / 1000 * 60 * 60) | number : '1.0-0' }} km/h</span>
|
||||
</div>
|
||||
<div class="col-sm-3 col-xs-12 text-center">
|
||||
<h6>Moyenne Verticale</h6>
|
||||
<span class="text-primary fw-medium fs-5">{{ (jump.x2data.speed.underCanopy.vertical.avg / 1000 * 60 * 60) | number : '1.0-0' }} km/h</span>
|
||||
</div>
|
||||
<div class="col-sm-3 col-xs-12 text-center">
|
||||
<h6>Moyenne Horizontale</h6>
|
||||
<span class="text-primary fw-medium fs-5">{{ (jump.x2data.speed.underCanopy.horizontal.avg / 1000 * 60 * 60) | number : '1.0-0' }} km/h</span>
|
||||
</div>
|
||||
</div>
|
||||
<mat-divider></mat-divider>
|
||||
|
||||
<div class="row mt-3">
|
||||
<div class="col-xs-12 text-center">
|
||||
<h5 class="d-flex justify-content-center mb-3"><mat-icon fontIcon="square_foot" class="me-1"></mat-icon> Finesse</h5>
|
||||
</div>
|
||||
<div class="col-sm-6 col-xs-12 text-center">
|
||||
<h6>Finesse en chute</h6>
|
||||
</div>
|
||||
<div class="col-sm-6 col-xs-12 text-center">
|
||||
<h6>Finesse sous voile</h6>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col-sm-3 col-xs-12 text-center">
|
||||
<h6>Finesse Max</h6>
|
||||
<span class="text-primary fw-medium fs-5">{{ jump.x2data.glideRatio.freeFall.max | number : '1.2-2' }}</span>
|
||||
</div>
|
||||
<div class="col-sm-3 col-xs-12 text-center">
|
||||
<h6>Finesse Moyenne</h6>
|
||||
<span class="text-primary fw-medium fs-5">{{ jump.x2data.glideRatio.freeFall.avg | number : '1.2-2' }}</span>
|
||||
</div>
|
||||
<div class="col-sm-3 col-xs-12 text-center">
|
||||
<h6>Finesse Max</h6>
|
||||
<span class="text-primary fw-medium fs-5">{{ jump.x2data.glideRatio.underCanopy.max | number : '1.2-2' }}</span>
|
||||
</div>
|
||||
<div class="col-sm-3 col-xs-12 text-center">
|
||||
<h6>Finesse Moyenne</h6>
|
||||
<span class="text-primary fw-medium fs-5">{{ jump.x2data.glideRatio.underCanopy.avg | number : '1.2-2' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<mat-divider></mat-divider>
|
||||
|
||||
<div class="row my-3">
|
||||
<div class="col-xs-12 text-center">
|
||||
<h5 class="d-flex justify-content-center mb-3"><mat-icon fontIcon="straighten" class="me-1"></mat-icon> Distance</h5>
|
||||
</div>
|
||||
<div class="col-sm-3 col-xs-12 text-center">
|
||||
<h6>Distance au largage</h6>
|
||||
<span class="text-primary fw-medium fs-5">{{ jump.x2data.distance.exitFromLandingZone| number : '1.0-0' }} m</span>
|
||||
</div>
|
||||
<div class="col-sm-3 col-xs-12 text-center">
|
||||
<h6 class="d-flex justify-content-center"><mat-icon fontIcon="timer" class="me-1 fs-5"></mat-icon> Durée sous voile</h6>
|
||||
<span class="text-primary fw-medium fs-5">{{formatDuration(jump.x2data.duration.underCanopy)}}</span>
|
||||
</div>
|
||||
<div class="col-sm-3 col-xs-12 text-center">
|
||||
<h6>Distance totale</h6>
|
||||
<span class="text-primary fw-medium fs-5">{{ jump.x2data.distance.totalFlying | number : '1.0-0' }} m</span>
|
||||
</div>
|
||||
<div class="col-sm-3 col-xs-12 text-center">
|
||||
<h6>Distance horizontale</h6>
|
||||
<span class="text-primary fw-medium fs-5">{{ jump.x2data.distance.totalHorizontal | number : '1.0-0' }} m</span>
|
||||
</div>
|
||||
</div>
|
||||
<mat-divider></mat-divider>
|
||||
}
|
||||
@else {
|
||||
<div class="row my-4">
|
||||
<div class="col-xs-12 text-center">
|
||||
<h5 class="d-flex justify-content-center mb-4"><mat-icon fontIcon="people" class="me-1"></mat-icon> Participants ({{ jump.sautants.length + 1 }})</h5>
|
||||
@for (sautant of jump.sautants; track sautant) {
|
||||
<span class="badge rounded-pill bg-primary mb-2 me-1 pe-3 py-1 fw-normal text-dark">
|
||||
<mat-icon class="align-middle me-1" fontIcon="person"></mat-icon>
|
||||
{{ sautant }}
|
||||
</span>
|
||||
}
|
||||
<span class="badge rounded-pill bg-accent mb-2 me-1 pe-3 py-1 fw-normal text-dark">
|
||||
<mat-icon class="align-middle me-1" fontIcon="person"></mat-icon>
|
||||
{{ jump.author.username }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<mat-divider></mat-divider>
|
||||
<div class="d-flex justify-content-center my-3 fs-5"><mat-icon fontIcon="satellite_alt" class="me-2"></mat-icon> Aucune donnée GPS</div>
|
||||
<mat-divider></mat-divider>
|
||||
}
|
||||
</div>
|
||||
</mat-dialog-content>
|
||||
<mat-dialog-actions>
|
||||
<span class="flex-spacer"></span>
|
||||
<button mat-button (click)="closeDialog()" class="me-1">Fermer</button>
|
||||
</mat-dialog-actions>
|
||||
@@ -0,0 +1,37 @@
|
||||
import { Component, Inject } from '@angular/core';
|
||||
import { DatePipe, DecimalPipe } from '@angular/common';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatDialogRef, MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
|
||||
import { MatDividerModule } from '@angular/material/divider';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
|
||||
import { Jump } from 'src/app/core/models';
|
||||
|
||||
@Component({
|
||||
selector: 'app-jump-view-dialog',
|
||||
templateUrl: 'jump-view.dialog.html',
|
||||
standalone: true,
|
||||
imports: [
|
||||
DatePipe, DecimalPipe,
|
||||
MatButtonModule, MatDialogModule,
|
||||
MatDividerModule, MatIconModule
|
||||
]
|
||||
})
|
||||
export class JumpViewDialogComponent {
|
||||
|
||||
constructor(
|
||||
public dialogRef: MatDialogRef<JumpViewDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public jump: Jump
|
||||
) { }
|
||||
|
||||
closeDialog(): void {
|
||||
// close the dialog without result
|
||||
this.dialogRef.close();
|
||||
}
|
||||
|
||||
formatDuration(duration: number): string {
|
||||
const start = new Date(0);
|
||||
start.setSeconds(duration);
|
||||
return start.toISOString().substring(14, 19);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
<div @flyInOut>
|
||||
<div class="d-flex">
|
||||
<div class="flex-fill">
|
||||
<h1 class="mb-1">{{title}}</h1>
|
||||
<span [hidden]="!lastJump.numero" class="me-2 fs-6">
|
||||
Dernier saut enregistré : <span class="fs-5 mx-1 text-primary">{{ lastJump.numero }}<sup>ème</sup></span> à <span class="fs-5 mx-1 text-primary">{{ lastJump.lieu }}</span> le <span class="fs-5 mx-1 text-primary">{{ lastJump.date | date: 'dd/MM/yyyy' }}</span>
|
||||
</span>
|
||||
</div>
|
||||
<span class="flex-spacer"></span>
|
||||
<div class="align-self-center me-2">
|
||||
<button mat-icon-button (click)="openAddDialog()" aria-label="Ajouter">
|
||||
<mat-icon fontIcon="add_notes"></mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button [matMenuTriggerFor]="menuJump" aria-label="Menu Jump">
|
||||
<mat-icon fontIcon="more_vert"></mat-icon>
|
||||
</button>
|
||||
<mat-menu #menuJump="matMenu">
|
||||
<button mat-menu-item (click)="openAddDialog()">
|
||||
<mat-icon fontIcon="add_notes"></mat-icon>
|
||||
<span>Ajouter un saut</span>
|
||||
</button>
|
||||
@for (menuitem of menuItems.getMenuLogbook(); track menuitem) {
|
||||
@if (menuitem.type === 'link') {
|
||||
<button mat-menu-item [routerLink]="['/', menuitem.state]">
|
||||
<mat-icon [fontIcon]="menuitem.icon"></mat-icon>
|
||||
<span>{{ menuitem.name }}</span>
|
||||
</button>
|
||||
}
|
||||
}
|
||||
</mat-menu>
|
||||
</div>
|
||||
</div>
|
||||
<mat-divider class="my-3"></mat-divider>
|
||||
@if (jumpsToHundred() <= 20) {
|
||||
<div class="d-flex pb-2 pe-2">
|
||||
<span class="flex-spacer"></span>
|
||||
<mat-icon fontIcon="warning" color="teal"></mat-icon>
|
||||
<div class="text-end text-teal align-self-center ms-2">
|
||||
{{ lastJump.numero + jumpsToHundred() }}<sup>ème</sup> à venir dans {{ jumpsToHundred() }}
|
||||
{{ jumpsToHundred() > 1 ? 'sauts' : 'saut' }}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<mat-card>
|
||||
<mat-card-header class="p-0">
|
||||
<mat-expansion-panel class="w-100">
|
||||
<mat-expansion-panel-header class="bg-navy-light">
|
||||
<mat-panel-title>Filtrer les sauts</mat-panel-title>
|
||||
@if (jumpsCount < lastJump.numero) {
|
||||
<mat-panel-description>
|
||||
{{ jumpsCount }} {{ jumpsCount > 1 ? 'sauts' : 'saut' }} sur {{ lastJump.numero }} correspondent à la recherche
|
||||
</mat-panel-description>
|
||||
}
|
||||
</mat-expansion-panel-header>
|
||||
<div class="d-flex mt-3 mb-0">
|
||||
<div class="flex-fill">
|
||||
<div class="label-container d-flex justify-content-between mx-2">
|
||||
<label for="yearRange">Année du saut</label>
|
||||
<label for="yearRange">de <span class="fw-bold">{{listConfig.filters.yearRangeStart}}</span> à <span class="fw-bold">{{listConfig.filters.yearRangeEnd}}</span></label>
|
||||
</div>
|
||||
<mat-slider [min]="rangesMinMax.year.start" [max]="rangesMinMax.year.end" step="1" discrete>
|
||||
<input id="yearRange" matSliderStartThumb [(ngModel)]="listConfig.filters.yearRangeStart">
|
||||
<input matSliderEndThumb [(ngModel)]="listConfig.filters.yearRangeEnd">
|
||||
</mat-slider>
|
||||
</div>
|
||||
<div class="flex-fill">
|
||||
<div class="label-container d-flex justify-content-between mx-2">
|
||||
<label for="numeroRange">Numéro du saut</label>
|
||||
<label for="numeroRange">de <span class="fw-bold">{{listConfig.filters.numeroRangeStart}}</span> à <span class="fw-bold">{{listConfig.filters.numeroRangeEnd}}</span></label>
|
||||
</div>
|
||||
<mat-slider [min]="rangesMinMax.numero.start" [max]="rangesMinMax.numero.end" step="1" discrete>
|
||||
<input id="numeroRange" matSliderStartThumb [(ngModel)]="listConfig.filters.numeroRangeStart">
|
||||
<input matSliderEndThumb [(ngModel)]="listConfig.filters.numeroRangeEnd">
|
||||
</mat-slider>
|
||||
</div>
|
||||
<div class="flex-fill">
|
||||
<div class="label-container d-flex justify-content-between mx-2">
|
||||
<label for="tailleRange">Taille de la voile</label>
|
||||
<label for="tailleRange">de <span class="fw-bold">{{listConfig.filters.tailleRangeStart}}</span> ft<sup>2</sup> à <span class="fw-bold">{{listConfig.filters.tailleRangeEnd}}</span> ft<sup>2</sup></label>
|
||||
</div>
|
||||
<mat-slider [min]="rangesMinMax.taille.start" [max]="rangesMinMax.taille.end" step="1" discrete>
|
||||
<input id="tailleRange" matSliderStartThumb [(ngModel)]="listConfig.filters.tailleRangeStart">
|
||||
<input matSliderEndThumb [(ngModel)]="listConfig.filters.tailleRangeEnd">
|
||||
</mat-slider>
|
||||
</div>
|
||||
<div class="flex-fill">
|
||||
<div class="label-container d-flex justify-content-between mx-2">
|
||||
<label for="participantsRange">Taille du groupe</label>
|
||||
<label for="participantsRange">de <span class="fw-bold">{{listConfig.filters.participantsRangeStart}}</span> à <span class="fw-bold">{{listConfig.filters.participantsRangeEnd}}</span></label>
|
||||
</div>
|
||||
<mat-slider [min]="rangesMinMax.participants.start" [max]="rangesMinMax.participants.end" step="1" discrete>
|
||||
<input id="participantsRange" matSliderStartThumb [(ngModel)]="listConfig.filters.participantsRangeStart">
|
||||
<input matSliderEndThumb [(ngModel)]="listConfig.filters.participantsRangeEnd">
|
||||
</mat-slider>
|
||||
</div>
|
||||
<span class="flex-spacer"></span>
|
||||
</div>
|
||||
</mat-expansion-panel>
|
||||
</mat-card-header>
|
||||
<mat-card-content class="mt-2">
|
||||
<app-jump-table (countChange)="setTotalResults($event)" #jumpTable [config]="listConfig" [isUser]="isUser" [showAdd]="canModify" [title]="title">
|
||||
</app-jump-table>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</div>
|
||||
@@ -0,0 +1,11 @@
|
||||
.mat-expansion-panel {
|
||||
border-bottom-right-radius: initial;
|
||||
border-bottom-left-radius: initial;
|
||||
}
|
||||
.mat-mdc-slider {
|
||||
max-width: 300px;
|
||||
width: 100%;
|
||||
}
|
||||
.label-container {
|
||||
max-width: 284px;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { LogbookComponent } from './logbook.component';
|
||||
|
||||
describe('LogbookComponent', () => {
|
||||
let component: LogbookComponent;
|
||||
let fixture: ComponentFixture<LogbookComponent>;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [LogbookComponent]
|
||||
});
|
||||
fixture = TestBed.createComponent(LogbookComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,399 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { trigger, state, style, animate, transition } from '@angular/animations';
|
||||
import { Component, AfterContentChecked, OnInit, OnDestroy, computed, signal, Signal } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { ActivatedRoute, Router, RouterModule } from '@angular/router';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatCardModule } from '@angular/material/card';
|
||||
import { MatDividerModule } from '@angular/material/divider';
|
||||
import { MatDialog, MatDialogModule } from '@angular/material/dialog';
|
||||
import { MatExpansionModule } from '@angular/material/expansion';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatMenuModule } from '@angular/material/menu';
|
||||
import { MatSliderModule } from '@angular/material/slider';
|
||||
import { MatSnackBar, MatSnackBarConfig, MatSnackBarHorizontalPosition, MatSnackBarModule, MatSnackBarVerticalPosition } from '@angular/material/snack-bar';
|
||||
import { Observable, Subscription } from 'rxjs';
|
||||
import { take } from 'rxjs/operators';
|
||||
|
||||
import { JumpAddDialogComponent } from 'src/app/components/logbook/dialogs';
|
||||
import { MenuItems, JumpTableComponent } from 'src/app/components/shared';
|
||||
import { Errors, Jump, JumpFile, JumpFileType, JumpListConfig, Range, User, X2Data } from 'src/app/core/models';
|
||||
import { JumpsService, JumpTableService, UserService } from 'src/app/core/services';
|
||||
import data from 'src/jumps.json';
|
||||
//import data from 'src/jumps_02.json';
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
imports: [
|
||||
CommonModule, RouterModule, FormsModule,
|
||||
MatButtonModule, MatCardModule, MatDividerModule, MatExpansionModule,
|
||||
MatIconModule, MatInputModule, MatMenuModule, MatDialogModule,
|
||||
MatSliderModule, MatSnackBarModule,
|
||||
JumpTableComponent
|
||||
],
|
||||
selector: 'app-logbook',
|
||||
templateUrl: './logbook.component.html',
|
||||
styleUrl: './logbook.component.scss',
|
||||
animations: [
|
||||
trigger('flyInOut', [
|
||||
state('in', style({ transform: 'translateY(0)' })),
|
||||
transition('void => *', [
|
||||
style({ transform: 'translateY(-100%)' }),
|
||||
animate(200)
|
||||
]),
|
||||
transition('* => void', [
|
||||
style({ transform: 'translateY(100%)' }),
|
||||
animate(200)
|
||||
])
|
||||
])
|
||||
]
|
||||
})
|
||||
export class LogbookComponent implements OnInit, OnDestroy, AfterContentChecked {
|
||||
private _currentUser: Subscription = new Subscription();
|
||||
private _subscriptions: Array<Subscription> = [];
|
||||
private _data: Subscription = new Subscription();
|
||||
private _lastjump: Subscription = new Subscription();
|
||||
private _lastjump$: Observable<Jump> = new Observable();
|
||||
public jumpsCount = 0;
|
||||
public errors!: Errors;
|
||||
public title = 'Carnet de sauts';
|
||||
public jump: Jump = {} as Jump;
|
||||
public lastJump: Jump = {} as Jump;
|
||||
public currentUser: User = {} as User;
|
||||
public listConfig: JumpListConfig = { type: 'all', filters: {} };
|
||||
public isAuthenticated = false;
|
||||
public isUser = true;
|
||||
public canModify = false;
|
||||
public isSubmitting = false;
|
||||
public tableRefresh = false;
|
||||
public jumpRefresh = false;
|
||||
public jumpsSinceHundred: Signal<number> = signal(0);
|
||||
public jumpsToHundred: Signal<number> = signal(100);
|
||||
public rangesMinMax: {
|
||||
numero: Range;
|
||||
taille: Range;
|
||||
participants: Range;
|
||||
hauteur: Range;
|
||||
year: Range;
|
||||
} = {
|
||||
numero: {start: 1, end: 100000},
|
||||
taille: {start: 1, end: 400},
|
||||
participants: {start: 1, end: 200},
|
||||
hauteur: {start: 0, end: 10000},
|
||||
year: {start: 1970, end: 2020}
|
||||
};
|
||||
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
private dialog: MatDialog,
|
||||
private snackBar: MatSnackBar,
|
||||
private _jumpsService: JumpsService,
|
||||
private _jumpTableService: JumpTableService,
|
||||
private _userService: UserService,
|
||||
public menuItems: MenuItems
|
||||
) { }
|
||||
|
||||
ngOnInit() {
|
||||
//this.importJumps();
|
||||
const data$: Observable<{isAuthenticated: boolean, lastjump: Jump}> = this.route.data as Observable<{isAuthenticated: boolean, lastjump: Jump}>;
|
||||
this._data = data$.subscribe((data: {isAuthenticated: boolean, lastjump: Jump}) => {
|
||||
this.isAuthenticated = data.isAuthenticated;
|
||||
if (!this.isAuthenticated) {
|
||||
this.router.navigateByUrl('/login');
|
||||
return;
|
||||
}
|
||||
|
||||
const currentUser$: Observable<User> = this._userService.currentUser;
|
||||
this._currentUser = currentUser$.subscribe((userData: User) => {
|
||||
this.currentUser = userData;
|
||||
this.canModify = this.currentUser.role === 'Admin';
|
||||
});
|
||||
|
||||
const currentYear: number = new Date().getFullYear();
|
||||
this.lastJump = data['lastjump'];
|
||||
this.rangesMinMax = {
|
||||
numero: {start: 1, end: data.lastjump.numero},
|
||||
taille: {start: data.lastjump.taille!, end: 230},
|
||||
participants: {start: 1, end: 20},
|
||||
hauteur: {start: 1000, end: 8000},
|
||||
year: {start: 2018, end: currentYear}
|
||||
}
|
||||
this.listConfig.filters.numeroRangeStart = this.rangesMinMax.numero.start;
|
||||
this.listConfig.filters.numeroRangeEnd = this.rangesMinMax.numero.end;
|
||||
this.listConfig.filters.tailleRangeStart = this.rangesMinMax.taille.start;
|
||||
this.listConfig.filters.tailleRangeEnd = this.rangesMinMax.taille.end;
|
||||
this.listConfig.filters.participantsRangeStart = this.rangesMinMax.participants.start;
|
||||
this.listConfig.filters.participantsRangeEnd = (this.rangesMinMax.participants.end / 2);
|
||||
this.listConfig.filters.hauteurRangeStart = this.rangesMinMax.hauteur.start;
|
||||
this.listConfig.filters.hauteurRangeEnd = this.rangesMinMax.hauteur.end;
|
||||
this.listConfig.filters.yearRangeStart = this.rangesMinMax.year.start;
|
||||
this.listConfig.filters.yearRangeEnd = this.rangesMinMax.year.end;
|
||||
this.jumpsSinceHundred = computed(() => (this.lastJump.numero % 100));
|
||||
this.jumpsToHundred = computed(() => (100 - this.jumpsSinceHundred()));
|
||||
/*
|
||||
this._lastjump$ = this._jumpsService.getLastJump();
|
||||
this._lastjump = this._lastjump$.subscribe((jump) => {
|
||||
const currentYear: number = new Date().getFullYear();
|
||||
this.lastJump = jump;
|
||||
this.rangesMinMax = {
|
||||
numero: {start: 1, end: jump.numero},
|
||||
taille: {start: jump.taille!, end: 230},
|
||||
participants: {start: 1, end: 20},
|
||||
hauteur: {start: 1000, end: 8000},
|
||||
year: {start: 2018, end: currentYear}
|
||||
}
|
||||
this.listConfig.filters.numeroRangeStart = this.rangesMinMax.numero.start;
|
||||
this.listConfig.filters.numeroRangeEnd = this.rangesMinMax.numero.end;
|
||||
this.listConfig.filters.tailleRangeStart = this.rangesMinMax.taille.start;
|
||||
this.listConfig.filters.tailleRangeEnd = this.rangesMinMax.taille.end;
|
||||
this.listConfig.filters.participantsRangeStart = this.rangesMinMax.participants.start;
|
||||
this.listConfig.filters.participantsRangeEnd = (this.rangesMinMax.participants.end / 2);
|
||||
this.listConfig.filters.hauteurRangeStart = this.rangesMinMax.hauteur.start;
|
||||
this.listConfig.filters.hauteurRangeEnd = this.rangesMinMax.hauteur.end;
|
||||
this.listConfig.filters.yearRangeStart = this.rangesMinMax.year.start;
|
||||
this.listConfig.filters.yearRangeEnd = this.rangesMinMax.year.end;
|
||||
this.jumpsSinceHundred = computed(() => (this.lastJump.numero % 100));
|
||||
this.jumpsToHundred = computed(() => (100 - this.jumpsSinceHundred()));
|
||||
});
|
||||
*/
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this._data.unsubscribe();
|
||||
this._currentUser.unsubscribe();
|
||||
this._lastjump.unsubscribe();
|
||||
this._subscriptions.forEach(subscription => {
|
||||
subscription.unsubscribe();
|
||||
});
|
||||
}
|
||||
|
||||
ngAfterContentChecked() {
|
||||
if (this.tableRefresh != this._jumpTableService.tableRefresh) {
|
||||
this.tableRefresh = this._jumpTableService.tableRefresh;
|
||||
this._lastjump$ = this._jumpsService.getLastJump();
|
||||
this._lastjump = this._lastjump$.subscribe((jump) => {
|
||||
this.lastJump = jump;
|
||||
});
|
||||
console.log('lastjump has been updated in logbook component');
|
||||
}
|
||||
}
|
||||
|
||||
importJumps() {
|
||||
try {
|
||||
data.forEach((entry) => {
|
||||
Object.assign(this.jump, entry);
|
||||
this._jumpsService.save(this.jump)
|
||||
.pipe(take(1))
|
||||
.subscribe({
|
||||
next: (jump) => {
|
||||
console.log(`Le saut n°${jump.numero} a été ajouté !`, 'OK');
|
||||
},
|
||||
error: (err) => {
|
||||
console.log(`Le saut n'a pas été ajouté !`, 'Error', err);
|
||||
this.errors = err;
|
||||
}
|
||||
});
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
openAddDialog(): void {
|
||||
this.jump = <Jump>{};
|
||||
this.jump.numero = (this.lastJump.numero + 1);
|
||||
this.jump.sautants = [];
|
||||
this.jump.participants = 1;
|
||||
const dialogRef = this.dialog.open(JumpAddDialogComponent, {
|
||||
width: '70vw',
|
||||
data: {jump: this.jump, lastJump: this.lastJump}
|
||||
});
|
||||
dialogRef.afterClosed().pipe(take(1))
|
||||
.subscribe(result => {
|
||||
if (result != undefined) {
|
||||
this._onEntry(result);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setTotalResults(value: number): void {
|
||||
this.jumpsCount = value;
|
||||
}
|
||||
|
||||
private _loadSkydiverIdJumps(jumps: Array<Jump>) {
|
||||
const config: JumpListConfig = { type: 'all', filters: {} } as JumpListConfig;
|
||||
config.filters.limit = 200;
|
||||
config.filters.offset = 0;
|
||||
config.filters.dateRangeStart = `${jumps[0].date!.substring(0, 10)} 00:00:00`;
|
||||
config.filters.dateRangeEnd = `${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 = jumps.length - data.items.length;
|
||||
if (diff < 0) {
|
||||
console.error(`${diff} ${diff > 1 ? 'données' : 'donnée'} altimètre de trop le ${jumps[0].date!.substring(0, 10)}`);
|
||||
}
|
||||
if (diff > 0) {
|
||||
console.error(`${diff}/${jumps.length} ${diff > 1 ? 'données altimètre manquantes' : 'donnée altimètre manquante'} le ${jumps[0].date!.substring(0, 10)}`, jumps, data.items);
|
||||
}
|
||||
console.log(`${jumps.length} ${jumps.length > 1 ? 'sauts' : 'saut'} le ${data.items[0].date!.substring(0, 10)}`, jumps.sort((a, b) => b.numero! - a.numero!), data.items);
|
||||
this._addX2Data(data.items, jumps);
|
||||
} else {
|
||||
console.info('Aucune données altimètre pour le ', jumps[0].date!.substring(0, 10));
|
||||
}
|
||||
},
|
||||
error: (err) => {
|
||||
console.error('getAllFromSkydiverIdApi error');
|
||||
this.errors = err;
|
||||
}
|
||||
});
|
||||
this._subscriptions.push(subscription);
|
||||
}
|
||||
|
||||
private _addJump(jumps: Array<Jump>, index: number = 0) {
|
||||
this._jumpsService.save(jumps[index])
|
||||
.pipe(take(1))
|
||||
.subscribe({
|
||||
next: (jump) => {
|
||||
jumps[index] = jump;
|
||||
console.log('Ajout du saut', jump.numero);
|
||||
Object.assign(this.jump, jump);
|
||||
this._openSnackBar(`Le saut n°${jump.numero} a été ajouté !`, 'Annuler');
|
||||
this._resetErrors();
|
||||
if (jumps.length == (index + 1)) {
|
||||
this._loadSkydiverIdJumps(jumps);
|
||||
} else {
|
||||
this._addJump(jumps, (index + 1));
|
||||
}
|
||||
},
|
||||
error: (err) => {
|
||||
this.errors = err;
|
||||
this.isSubmitting = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private _addX2Data(x2data: Array<X2Data>, jumps: Array<Jump>, index: number = 0) {
|
||||
const file: JumpFile = {
|
||||
name: x2data[index].name,
|
||||
path: `/Volumes/Storage/Skydive/X2_Logs/${x2data[index].date!.substring(0, 4)}/`,
|
||||
type: JumpFileType.CSV
|
||||
} as JumpFile;
|
||||
this._subscriptions.push(
|
||||
this._jumpsService.saveX2Data(jumps[index].slug!, file, x2data[index])
|
||||
.pipe(take(1))
|
||||
.subscribe({
|
||||
next: (jump) => {
|
||||
jumps[index] = jump;
|
||||
console.log(`Le fichier ${file.name} a été ajouté au saut n°${jump.numero} !`, 'OK');
|
||||
if (jumps.length == (index + 1) || x2data.length == (index + 1)) {
|
||||
this._addKmlFile(jumps);
|
||||
} else {
|
||||
this._addX2Data(x2data, jumps, (index + 1));
|
||||
}
|
||||
|
||||
},
|
||||
error: (err) => {
|
||||
this.errors = err;
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
private _addKmlFile(jumps: Array<Jump>, index: number = 0) {
|
||||
if (jumps[index].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: jumps[index].x2data.name.substring(0, -4) + '.kml',
|
||||
path: `/Volumes/Storage/Skydive/X2_Kmls/${jumps[index].date.substring(0, 4)}/`,
|
||||
type: JumpFileType.KML
|
||||
} as JumpFile;
|
||||
this._subscriptions.push(
|
||||
this._jumpsService.saveKml(jumps[index].slug, file)
|
||||
.pipe(take(1))
|
||||
.subscribe({
|
||||
next: (file) => {
|
||||
console.log(`Le fichier ${file.path}${file.name} a été ajouté au saut n°${jumps[index].numero} !`, 'OK');
|
||||
if (jumps.length == (index + 1)) {
|
||||
this._resetErrors();
|
||||
this.jumpRefresh = !this.jumpRefresh;
|
||||
this._jumpTableService.updateJumpRefresh(this.jumpRefresh);
|
||||
this._lastjump$ = this._jumpsService.getLastJump();
|
||||
this._lastjump = this._lastjump$.subscribe((jump) => {
|
||||
this.lastJump = jump;
|
||||
});
|
||||
} else {
|
||||
this._addKmlFile(jumps, (index + 1));
|
||||
}
|
||||
},
|
||||
error: (err) => {
|
||||
this.errors = err;
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private _onEntry(jumps: Array<Jump>): void {
|
||||
if (jumps.length) {
|
||||
this._addJump(jumps);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
private _onEntry(jumps: Array<Jump>): void {
|
||||
const max: number = jumps.length;
|
||||
let refresh: boolean = false;
|
||||
jumps.forEach((jump: Jump, index: number) => {
|
||||
console.log('Ajout du saut', jump.numero);
|
||||
Object.assign(this.jump, jump);
|
||||
if (max == index+1) {
|
||||
refresh = true;
|
||||
}
|
||||
this._saveJump(jump, refresh);
|
||||
});
|
||||
}
|
||||
|
||||
private _saveJump(jump: Jump, refresh: boolean) {
|
||||
this._jumpsService.save(jump)
|
||||
.pipe(take(1))
|
||||
.subscribe({
|
||||
next: (jump) => {
|
||||
this._openSnackBar(`Le saut n°${jump.numero} a été ajouté !`, 'Annuler');
|
||||
this._resetErrors();
|
||||
if (refresh) {
|
||||
this.jumpRefresh = !this.jumpRefresh;
|
||||
this._jumpTableService.updateJumpRefresh(this.jumpRefresh);
|
||||
this._lastjump$ = this._jumpsService.getLastJump();
|
||||
this._lastjump = this._lastjump$.subscribe((jump) => {
|
||||
this.lastJump = jump;
|
||||
});
|
||||
}
|
||||
},
|
||||
error: (err) => {
|
||||
this.errors = err;
|
||||
this.isSubmitting = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
*/
|
||||
|
||||
private _openSnackBar(content: string, title: string) {
|
||||
const config: MatSnackBarConfig = {
|
||||
horizontalPosition: <MatSnackBarHorizontalPosition>'end',
|
||||
verticalPosition: <MatSnackBarVerticalPosition>'bottom',
|
||||
duration: 4000
|
||||
};
|
||||
this.snackBar.open(content, title, config);
|
||||
}
|
||||
|
||||
private _resetErrors(): void {
|
||||
this.errors = { errors: {} };
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user